diff --git a/.eslintrc.json b/.eslintrc.json
index 6f7a0eccf..c6d98d2c6 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -4,7 +4,8 @@
"extends": ["eslint:recommended", "prettier", "plugin:@typescript-eslint/recommended"], // this is optional
"env": {
"browser": true,
- "node": true
+ "node": true,
+ "jest": true
},
"settings": {
"react": {
@@ -166,5 +167,9 @@
],
"symbol-description": "error",
"yoda": "error"
+ },
+ "globals": {
+ "cy": "readonly",
+ "Cypress": "readonly"
}
}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..8f7a92ae3
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,19 @@
+version: 2
+updates:
+ - package-ecosystem: 'npm'
+ directory: '/'
+ schedule:
+ interval: 'weekly'
+ target-branch: 'develop'
+
+ - package-ecosystem: 'npm'
+ directory: '/gallery'
+ schedule:
+ interval: 'weekly'
+ target-branch: 'develop'
+
+ - package-ecosystem: 'npm'
+ directory: '/docs'
+ schedule:
+ interval: 'weekly'
+ target-branch: 'develop'
diff --git a/.github/workflows/master-deployment.yml b/.github/workflows/master-deployment.yml
index 862ca50a6..820b12d8c 100644
--- a/.github/workflows/master-deployment.yml
+++ b/.github/workflows/master-deployment.yml
@@ -31,7 +31,7 @@ jobs:
with:
build: yarn run build
start: yarn run dev
- wait-on: "http://localhost:3000"
+ wait-on: 'http://localhost:3000'
browser: chrome
build-s3:
needs: build-test
@@ -79,7 +79,7 @@ jobs:
context: .
file: ./Dockerfile
push: true
- tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.2
+ tags: ${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_LABS_USERNAME }}/neodash:2.4.9
build-docker-legacy:
needs: build-test
runs-on: neodash-runners
@@ -103,7 +103,7 @@ jobs:
context: .
file: ./Dockerfile
push: true
- tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.2
+ tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.4.9
deploy-gallery:
runs-on: neodash-runners
strategy:
diff --git a/.gitignore b/.gitignore
index 49f853b33..56c2e7ad8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,7 +15,7 @@ target
/coverage
/.nyc_output
cypress/videos
-
+cypress/screenshots
# production
/build
/dist
diff --git a/Dockerfile b/Dockerfile
index 45c42b194..a31be8d85 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,6 +10,7 @@ WORKDIR /usr/local/src/neodash
# Copy sources and install/build
COPY ./package.json /usr/local/src/neodash/package.json
+COPY ./yarn.lock /usr/local/src/neodash/yarn.lock
RUN yarn install
COPY ./ /usr/local/src/neodash
@@ -43,4 +44,4 @@ USER nginx
EXPOSE $NGINX_PORT
HEALTHCHECK cmd curl --fail "http://localhost:$NGINX_PORT" || exit 1
-LABEL version="2.4.2"
+LABEL version="2.4.9"
diff --git a/README.md b/README.md
index 5c4db07c9..7ba0e71da 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,24 @@
-## NeoDash - Neo4j Dashboard Builder
-NeoDash is an open source tool for visualizing your Neo4j data. It lets you group visualizations together as dashboards, and allow for interactions between reports.
+## NeoDash Labs - Neo4j Dashboard Builder
-![screenshot](public/screenshot.png)
+![screenshot](evolving.png)
+
+In September 2024 **Neo4j [announced](https://www.datanami.com/2024/09/04/neo4j-simplifies-graph-database-in-the-cloud/#:~:text=NeoDash%20is%20an%20open%20source,was%20open%20source%2C%20not%20supported) NeoDash is evolving into a fully supported dashboard builder, as part of the Neo4j product suite**.
+
+This project (NeoDash Labs) will still be available and contain experimental features, but will **not** have official support. If you're interested to get official support for NeoDash as part of a Neo4j License agreement, please reach out to your Neo4j contact person.
+
+## About NeoDash Labs
+NeoDash is a web-based tool for visualizing your Neo4j data. It lets you group visualizations together as dashboards, and allow for interactions between reports.
Neodash supports presenting your data as tables, graphs, bar charts, line charts, maps and more. It contains a Cypher editor to directly write the Cypher queries that populate the reports. You can save dashboards to your database, and share them with others.
-## Try NeoDash
+## Try NeoDash Labs
You can run NeoDash in one of three ways:
-1. You can install NeoDash into Neo4j Desktop from the [graph app gallery](https://install.graphapp.io). NeoDash will automatically connect to your active database.
-2. You can run NeoDash from a web browser by visiting http://neodash.graphapp.io.
+1. You can install NeoDash Labs into Neo4j Desktop from the [graph app gallery](https://install.graphapp.io). NeoDash will automatically connect to your active database.
+> Note: never versions of Neo4j Desktop do not support adding experimental graph apps such as NeoDash.
+
+2. You can run NeoDash Labs from a web browser by visiting http://neodash.graphapp.io.
3. For on-prem deployments, you can build the application yourself, or pull the latest Docker image from Docker Hub.
```
# Run the application on http://localhost:5005
@@ -79,4 +87,4 @@ If you have any questions about NeoDash, please reach out to the maintainers:
- Connect with us on the [Neo4j Discord](https://neo4j.com/developer/discord/).
- Create a post on the Neo4j [Community Forum](https://community.neo4j.com/).
-> NeoDash is a free and open-source tool developed by the Neo4j community - not an official Neo4j product. If you have a need for a commercial agreement around training, custom extensions or other services, please contact the [Neo4j Professional Services](https://neo4j.com/professional-services/) team.
\ No newline at end of file
+> NeoDash Labs is a free and open-source tool developed by the Neo4j community - not an official Neo4j product. Use at your own risk!
\ No newline at end of file
diff --git a/changelog.md b/changelog.md
index fd35b5bf1..9227e1fff 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,103 @@
+## NeoDash 2.4.9
+This release adds some minor changes to documentation and implements some community contributions.
+- Added notice about project evolution: [#967](https://github.com/neo4j-labs/neodash/pull/967)
+- Added community contributions and bug fixes:
+[#967](https://github.com/neo4j-labs/neodash/pull/967)
+[#894](https://github.com/neo4j-labs/neodash/pull/894)
+[#822](https://github.com/neo4j-labs/neodash/pull/822)
+[#951](https://github.com/neo4j-labs/neodash/pull/951)
+[#946](https://github.com/neo4j-labs/neodash/pull/946)
+[#944](https://github.com/neo4j-labs/neodash/pull/944)
+[#943](https://github.com/neo4j-labs/neodash/pull/943)
+[#938](https://github.com/neo4j-labs/neodash/pull/938)
+[#935](https://github.com/neo4j-labs/neodash/pull/935)
+[#918](https://github.com/neo4j-labs/neodash/pull/918)
+[#908](https://github.com/neo4j-labs/neodash/pull/908)
+[#906](https://github.com/neo4j-labs/neodash/pull/906)
+[#902](https://github.com/neo4j-labs/neodash/pull/902)
+[#895](https://github.com/neo4j-labs/neodash/pull/895)
+[#893](https://github.com/neo4j-labs/neodash/pull/893)
+
+## NeoDash 2.4.8
+This is a minor release containing an important fix and other minor fixes:
+
+- Fixed a bug where loading a dashboard would reset parameters to null ([887](https://github.com/neo4j-labs/neodash/pull/887)).
+- Fix relationship width parameter for Graph report ([889](https://github.com/neo4j-labs/neodash/pull/889)).
+
+Thanks to all the contributors for this release:
+- [alfredorubin96](https://github.com/alfredorubin96),
+- [nielsdejong](https://github.com/nielsdejong).
+
+## NeoDash 2.4.7
+This is a minor release containing a few critical fixes and general code quality improvements:
+
+- Fix multiple parameter select ([881](https://github.com/neo4j-labs/neodash/pull/881)).
+- Fix parameter casting error when loading dashboards([874](https://github.com/neo4j-labs/neodash/pull/874)).
+- Fix the fraud demo in the [Example Gallery](https://neodash-gallery.graphapp.io/).
+
+Thanks to all the contributors for this release:
+- [alfredorubin96](https://github.com/alfredorubin96),
+- [MariusC](https://github.com/mariusconjeaud),
+- [elizarp](https://github.com/elizarp).
+
+## NeoDash 2.4.6
+This is a minor release containing a few critical fixes and some extra style customizations:
+
+- Fix bad text wrapping for arrays in tables ([868](https://github.com/neo4j-labs/neodash/pull/868)).
+- Make wrapping in table optional, disabled by default ([872](https://github.com/neo4j-labs/neodash/pull/872)).
+- Fixed issues where cross database dashboard sharing always reverted back to the default database ([873](https://github.com/neo4j-labs/neodash/pull/873)).
+- Added option to define style config using environment variables for the Docker image ([876](https://github.com/neo4j-labs/neodash/pull/876)).
+
+## NeoDash 2.4.5
+This is a small release containing a few fixes:
+- Fixed rendering of string arrays inside tables, report titles, and report action buttons [849](https://github.com/neo4j-labs/neodash/pull/849)
+- Allowed text to wrap in tables, preserving the number of rows [852](https://github.com/neo4j-labs/neodash/pull/852)
+- Disabled auto-sorting of Cypher query-based Parameter Select ; use Cypher ORDER BY to control result order [857](https://github.com/neo4j-labs/neodash/pull/857)
+- Updated role selector menu, and made user updates more robust [854](https://github.com/neo4j-labs/neodash/pull/854)
+
+Thanks to all the contributors for this release:
+- [MariusC](https://github.com/mariusconjeaud),
+- [LiamEdwardsLamarche](https://github.com/LiamEdwardsLamarche),
+- [AleSim94](https://github.com/AleSim94)
+
+## NeoDash 2.4.4
+This is a hotfix release fixing some breaking issues in the 2.4.3:
+- Fixed number parsing using newer versions of the Neo4j driver. [811](https://github.com/neo4j-labs/neodash/pull/811)
+- Reverted new connection handler for auto-renewed SSO sessions. [815](https://github.com/neo4j-labs/neodash/pull/815)
+- Improved handling of parameters in form extension, resolved local state issues. [813](https://github.com/neo4j-labs/neodash/pull/813)
+- Updated Role management extension to no longer execute queries in parallel, improved UX and error handling [813](https://github.com/neo4j-labs/neodash/pull/813)
+
+If you are currently using NeoDash version 2.4.3, we recommend updating as soon as possible.
+
+## NeoDash 2.4.3
+This release contains several improvements and additions to multi-dashboard management, as well as a bug fixes and a variety of quality-of-life improvements:
+
+Dashboard management and access control:
+- Added a UI for handling dashboard access using RBAC, as well as a new extension to simply access control.
+- Added button to sidebar to refresh the list of dashboards saved in the database.
+- Improved handling and detection of draft dashboards in the dashboard sidebar.
+
+Other improvements:
+- Changed CSV export functionality for tables to use UTF-8 format.
+- Various improvements / fixes to the documentation to include new images, and up-to-date functionality.
+- Added logic for handling refresh tokens when connected to NeoDash via SSO.
+- Incorporated tooltips for bar charts with and without custom labels.
+
+Bug fixes and testing:
+- Implemented bug fixes on type casting for numeric parameter selectors.
+- Fixed issue with report actions not functioning properly on node click events.
+- Extended test suite with Cypress tests for advanced settings in the bar chart.
+
+Thanks to all the contributors for this release:
+- [OskarDamkjaer](https://github.com/OskarDamkjaer)
+- [alfredorubin96](https://github.com/alfredorubin96),
+- [AleSim94](https://github.com/AleSim94),
+- [BennuFire](https://github.com/BennuFire),
+- [jacobbleakley-neo4j](https://github.com/jacobbleakley-neo4j),
+- [josepmonclus](https://github.com/josepmonclus)
+- [nielsdejong](https://github.com/nielsdejong)
+
+
## NeoDash 2.4.2
This is a release with a large amount of quality of life improvements, as well as some new features:
diff --git a/cypress.config.ts b/cypress.config.ts
index 73948909e..93f8cffdf 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -5,6 +5,7 @@ export default defineConfig({
projectId: 'a8nh14',
video: false,
e2e: {
+ defaultCommandTimeout: 20000,
experimentalMemoryManagement: true,
numTestsKeptInMemory: 0,
baseUrl: 'http://localhost:3000',
diff --git a/cypress/Page.js b/cypress/Page.js
new file mode 100644
index 000000000..f90742a41
--- /dev/null
+++ b/cypress/Page.js
@@ -0,0 +1,156 @@
+const DB_URL = 'localhost';
+const DB_USERNAME = 'neo4j';
+const DB_PASSWORD = 'test1234';
+
+export class Page {
+ constructor(cardSelector) {
+ this.cardSelector = cardSelector;
+ }
+
+ init() {
+ cy.viewport(1920, 1080);
+ cy.visit('/', {
+ onBeforeLoad(win) {
+ win.localStorage.clear();
+ },
+ });
+ return this;
+ }
+
+ createNewDashboard() {
+ cy.get('#form-dialog-title').then(($div) => {
+ const text = $div.text();
+ if (text == 'NeoDash - Neo4j Dashboard Builder') {
+ cy.wait(100);
+ // Create new dashboard
+ cy.contains('New Dashboard').click();
+ }
+ });
+ return this;
+ }
+
+ connectToNeo4j() {
+ cy.get('#form-dialog-title', { timeout: 20000 }).should('contain', 'Connect to Neo4j');
+ cy.get('#url').clear().type(DB_URL);
+ cy.get('#dbusername').clear().type(DB_USERNAME);
+ cy.get('#dbpassword').type(DB_PASSWORD);
+ cy.get('button').contains('Connect').click();
+ cy.wait(100);
+ return this;
+ }
+
+ enableReportActions() {
+ cy.get('main button[aria-label="Extensions').should('be.visible').click();
+ cy.get('#checkbox-actions').scrollIntoView();
+ cy.get('#checkbox-actions').should('be.visible').click();
+ cy.get('.ndl-dialog-close').scrollIntoView().should('be.visible').click();
+ cy.wait(100);
+ return this;
+ }
+
+ enableAdvancedVisualizations() {
+ cy.get('main button[aria-label="Extensions').should('be.visible').click();
+ cy.get('#checkbox-advanced-charts').should('be.visible').click();
+ cy.get('.ndl-dialog-close').scrollIntoView().should('be.visible').click();
+ cy.wait(100);
+ return this;
+ }
+
+ enableFormsExtension() {
+ cy.get('main button[aria-label="Extensions').should('be.visible').click();
+ cy.get('#checkbox-forms').scrollIntoView();
+ cy.get('#checkbox-forms').should('be.visible').click();
+ cy.get('.ndl-dialog-close').scrollIntoView().should('be.visible').click();
+ cy.wait(100);
+ return this;
+ }
+
+ selectReportOfType(type) {
+ cy.get('main .react-grid-item button[aria-label="add report"]').should('be.visible').click();
+ cy.get('main .react-grid-item')
+ .contains('No query specified.')
+ .parentsUntil('.react-grid-item')
+ .find('button[aria-label="settings"]', { timeout: 2000 })
+ .should('be.visible')
+ .click();
+ cy.get(`${this.cardSelector} #type`, { timeout: 2000 }).should('be.visible').click();
+ cy.contains(type).click();
+ cy.wait(100);
+ return this;
+ }
+
+ createReportOfType(type, query, fast = false, run = true) {
+ this.selectReportOfType(type);
+ if (fast) {
+ cy.get(`${this.cardSelector} .ReactCodeMirror`).type(query, {
+ delay: 1,
+ parseSpecialCharSequences: false,
+ });
+ } else {
+ cy.get(`${this.cardSelector} .ReactCodeMirror`).type(query, { parseSpecialCharSequences: false });
+ }
+ cy.wait(400);
+
+ if (run) {
+ this.closeSettings();
+ }
+
+ cy.wait(100);
+ return this;
+ }
+
+ openSettings() {
+ cy.get(this.cardSelector).find('button[aria-label="settings"]', { WAITING_TIME: 2000 }).click();
+ cy.wait(100);
+ return this;
+ }
+
+ closeSettings() {
+ cy.get(`${this.cardSelector} button[aria-label="run"]`).click();
+ cy.wait(100);
+ return this;
+ }
+
+ openAdvancedSettings() {
+ this.openSettings();
+ cy.get(this.cardSelector).contains('Advanced settings').click();
+ cy.wait(100);
+ return this;
+ }
+
+ closeAdvancedSettings() {
+ cy.get(this.cardSelector).contains('Advanced settings').click();
+ this.closeSettings();
+ return this;
+ }
+
+ openReportActionsMenu() {
+ this.openSettings();
+ cy.get(this.cardSelector).find('button[aria-label="custom actions"]').click();
+ cy.wait(100);
+ return this;
+ }
+
+ updateDropdownAdvancedSetting(settingLabel, targetValue) {
+ this.openAdvancedSettings();
+ cy.get(`${this.cardSelector} .ndl-dropdown`).contains(settingLabel).siblings('div').click();
+ cy.contains(targetValue).click();
+ this.closeAdvancedSettings();
+ return this;
+ }
+
+ updateChartQuery(query) {
+ this.openSettings();
+
+ cy.get(this.cardSelector)
+ .find('.ndl-cypher-editor div[role="textbox"]')
+ .should('be.visible')
+ .click()
+ .clear()
+ .type(query);
+ cy.wait(100);
+
+ this.closeSettings();
+ return this;
+ }
+}
diff --git a/cypress/e2e/charts/array.cy.js b/cypress/e2e/charts/array.cy.js
new file mode 100644
index 000000000..1da231f01
--- /dev/null
+++ b/cypress/e2e/charts/array.cy.js
@@ -0,0 +1,115 @@
+import { stringArrayCypherQuery, intArrayCypherQuery, pathArrayCypherQuery } from '../../fixtures/cypher_queries';
+import { Page } from '../../Page';
+
+const CARD_SELECTOR = 'main .react-grid-item:eq(2)';
+const page = new Page(CARD_SELECTOR);
+
+// Ignore warnings that may appear when using the Cypress dev server
+Cypress.on('uncaught:exception', (err, runnable) => {
+ console.log(err, runnable);
+ return false;
+});
+
+describe('Testing array rendering', () => {
+ beforeEach('open neodash', () => {
+ page.init().createNewDashboard().connectToNeo4j();
+ cy.wait(100);
+ });
+
+ it('creates a table that contains string arrays', () => {
+ cy.checkInitialState();
+ page.enableReportActions();
+ page.createReportOfType('Table', stringArrayCypherQuery, true, true);
+
+ // Standard array, displays strings joined with comma and whitespace
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0)`).should('have.text', 'initial, list');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(1)`).should('have.text', 'other, list');
+
+ // Now, transpose the table
+ page.updateDropdownAdvancedSetting('Transpose Rows & Columns', 'on');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-columnHeaderTitle:eq(1)`).should('have.text', 'initial,list');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(1)`).should('have.text', 'other, list');
+
+ // Transpose back
+ // And add a report action
+ page.updateDropdownAdvancedSetting('Transpose Rows & Columns', 'off');
+ page.openReportActionsMenu();
+ cy.get('.ndl-modal').find('button[aria-label="add"]').click();
+ cy.get('.ndl-modal').find('input:eq(2)').type('column');
+ cy.get('.ndl-modal').find('input:eq(5)').type('test_param');
+ cy.get('.ndl-modal').find('input:eq(6)').type('column');
+ cy.get('.ndl-modal').find('button').contains('Save').click();
+ page.closeSettings();
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0)`)
+ .find('button')
+ .should('be.visible')
+ .should('have.text', 'initial, list')
+ .click();
+
+ // Previous step's click set a parameter from the array
+ // Test that parameter rendering works
+ cy.get(`${CARD_SELECTOR} .MuiCardHeader-root`).find('input').type('$neodash_test_param').blur();
+ cy.get(`${CARD_SELECTOR} .MuiCardHeader-root`).find('input').should('have.value', 'initial, list');
+ });
+
+ it('creates a table that contains int arrays', () => {
+ cy.checkInitialState();
+ page.createReportOfType('Table', intArrayCypherQuery, true, true);
+
+ // Standard array, displays strings joined with comma and whitespace
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0)`).should('have.text', '1, 2');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(1)`).should('have.text', '3, 4');
+
+ // Now, transpose the table
+ page.updateDropdownAdvancedSetting('Transpose Rows & Columns', 'on');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-columnHeaderTitle:eq(1)`).should('have.text', '1,2');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(1)`).should('have.text', '3, 4');
+ });
+
+ it('creates a table that contains nodes and rels', () => {
+ cy.checkInitialState();
+ page.createReportOfType('Table', pathArrayCypherQuery, true, true);
+
+ // Standard array, displays a path with two nodes and a relationship
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0)`).should('have.text', 'PersonACTED_INMovie');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0) button`).should('have.length', 2);
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0) button:eq(0)`).should('have.text', 'Person');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0) button:eq(1)`).should('have.text', 'Movie');
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0) .MuiChip-root`).should('have.length', 1);
+ cy.get(`${CARD_SELECTOR} .MuiDataGrid-cell:eq(0) .MuiChip-root`).should('have.text', 'ACTED_IN');
+ });
+
+ it('creates a single value report which is an array', () => {
+ cy.checkInitialState();
+ page.createReportOfType('Single Value', stringArrayCypherQuery, true, true);
+ cy.get(CARD_SELECTOR).should('have.text', 'initial, list');
+ });
+
+ it('creates a multi parameter select', () => {
+ cy.checkInitialState();
+ page.selectReportOfType('Parameter Select');
+ cy.get('main .react-grid-item:eq(2) label[for="Selection Type"]').siblings('div').click();
+ // Set up the parameter select
+ cy.contains('Node Property').click();
+ cy.wait(100);
+ cy.contains('Node Label').click();
+ cy.contains('Node Label').siblings('div').find('input').type('Movie');
+ cy.wait(1000);
+ cy.get('.MuiAutocomplete-popper').contains('Movie').click();
+ cy.contains('Property Name').click();
+ cy.contains('Property Name').siblings('div').find('input').type('title');
+ cy.wait(1000);
+ cy.get('.MuiAutocomplete-popper').contains('title').click();
+ // Enable multiple selection
+ page.closeSettings();
+ page.updateDropdownAdvancedSetting('Multiple Selection', 'on');
+ // Finally, select a few values in the parameter select
+ cy.get(CARD_SELECTOR).contains('Movie title').click();
+ cy.get(CARD_SELECTOR).contains('Movie title').siblings('div').find('input').type('a');
+ cy.get('.MuiAutocomplete-popper').contains('Apollo 13').click();
+ cy.get(CARD_SELECTOR).contains('Movie title').siblings('div').find('input').type('t');
+ cy.get('.MuiAutocomplete-popper').contains('The Matrix').click();
+ cy.get(CARD_SELECTOR).contains('Apollo 13').should('be.visible');
+ cy.get(CARD_SELECTOR).contains('The Matrix').should('be.visible');
+ });
+});
diff --git a/cypress/e2e/charts/bar.cy.js b/cypress/e2e/charts/bar.cy.js
new file mode 100644
index 000000000..80f13d640
--- /dev/null
+++ b/cypress/e2e/charts/bar.cy.js
@@ -0,0 +1,211 @@
+import { barChartCypherQuery } from '../../fixtures/cypher_queries';
+import { Page } from '../../Page';
+
+const CARD_SELECTOR = '.react-grid-layout:eq(0) .MuiGrid-root:eq(2)';
+const page = new Page(CARD_SELECTOR);
+
+// Ignore warnings that may appear when using the Cypress dev server
+Cypress.on('uncaught:exception', (err, runnable) => {
+ console.log(err, runnable);
+ return false;
+});
+
+describe('Testing bar chart', () => {
+ beforeEach('open neodash', () => {
+ page.init().createNewDashboard().connectToNeo4j().createReportOfType('Bar Chart', barChartCypherQuery);
+ });
+
+ it('Checking Colour Picker settings', () => {
+ //Opens advanced settings
+ cy.get('.react-grid-layout')
+ .first()
+ .within(() => {
+ //Finds the 2nd card
+ cy.get('.MuiGrid-root:eq(2)').within(() => {
+ // Access advanced settings
+ cy.get('button').eq(1).click();
+ cy.get('[role="switch"]').click();
+ cy.wait(200);
+ // Changing setting for colour picker
+ cy.get('[data-testid="colorpicker-input"]').find('input').click().type('{selectall}').type('red');
+ cy.get('button[aria-label="run"]').click();
+ // Checking that colour picker was applied correctly
+ cy.get('.card-view').should('have.css', 'background-color', 'rgb(255, 0, 0)');
+ cy.wait(200);
+ // Changing colour back to white
+ cy.get('button').eq(1).click();
+ cy.get('[data-testid="colorpicker-input"]').find('input').click().type('{selectall}').type('white');
+ cy.get('button[aria-label="run"]').click();
+ // Checking colour has been set back to white
+ cy.wait(200);
+ cy.get('.card-view').should('have.css', 'background-color', 'rgb(255, 255, 255)');
+ });
+ });
+ });
+
+ it('Checking Selector Description', () => {
+ //Opens first 2nd card
+ cy.get('.react-grid-layout:eq(0) .MuiGrid-root:eq(2)').within(() => {
+ // Access advanced settings
+ cy.get('button').eq(1).click();
+ cy.get('[role="switch"]').click();
+ cy.wait(200);
+ // Changing Selector Description to 'Test'
+ cy.get('.ndl-textarea').contains('span', 'Selector Description').click().type('Test');
+ cy.get('button[aria-label="run"]').click();
+ // Pressing Selector Description button
+ cy.get('button[aria-label="details"]').click();
+ });
+ // Checking that Selector Description is behaving as expected
+ cy.get('.MuiDialog-paper').should('be.visible').and('contain.text', 'Test');
+ cy.wait(1000);
+
+ // Click elsewhere on the page to close dialog box
+ cy.get('div[role="dialog"]').parent().click(-100, -100, { force: true });
+ });
+
+ it('Checking full screen bar chart setting', () => {
+ page.updateDropdownAdvancedSetting('Fullscreen enabled', 'on');
+ cy.get('button[aria-label="maximize"]').click();
+ // Checking existence of full-screen modal
+ cy.get('.dialog-xxl').should('be.visible');
+ // Action to close full-screen modal
+ cy.get('button[aria-label="un-maximize"]').click();
+ // Checking that fullscreen has un-maximized
+ // Check that the div is no longer in the DOM
+ cy.get('div[data-focus-lock-disabled="false"]').should('not.exist');
+ });
+
+ it('Checking "Autorun Query" works as intended', () => {
+ page.updateDropdownAdvancedSetting('Auto-run query', 'off');
+ cy.get('.MuiCardContent-root').find('.ndl-cypher-editor').should('be.visible');
+ cy.get('.MuiCardContent-root').find('g').should('not.exist');
+ cy.wait(100);
+ cy.get('.MuiCardContent-root').find('button[aria-label="run"]').filter(':visible').click();
+ cy.get('g').should('exist');
+ });
+
+ it('Checking Legend integration works as intended', () => {
+ page.updateDropdownAdvancedSetting('Show Legend', 'on');
+ // Checking that legend matches value specified: in the case - 'count'
+ cy.get('svg g g text').last().contains(/count/i);
+
+ page.updateDropdownAdvancedSetting('Show Legend', 'off');
+ cy.get('svg g g text').last().contains(/count/i).should('not.exist');
+ });
+
+ it('Checking the stacked grouping function works as intended', () => {
+ const TRANSLATE_REGEXP = /translate\(([0-9]{1,3}), [0-9]{1,3}\)/;
+
+ page
+ .updateChartQuery(
+ 'MATCH (p:Person)-[:DIRECTED]->(n:Movie) RETURN n.released AS released, p.name AS Director, count(n.title) AS count LIMIT 5'
+ )
+ .updateDropdownAdvancedSetting('Grouping', 'on');
+
+ cy.get('.MuiGrid-root:eq(2)')
+ .find('.ndl-dropdown:contains("Group")')
+ .find('svg')
+ .parent()
+ .click()
+ .type('Director{enter}');
+ // Checking that the groups are stacked
+ cy.get('.MuiGrid-root:eq(2)')
+ .find('g')
+ .children('g')
+ .eq(3) // Get the fourth g element (index starts from 0)
+ .invoke('attr', 'transform')
+ .then((transformValue) => {
+ // Captures the first number in the translate attribute using the parenthesis to capture the first digit and put it in the second value of the resulting array
+ // if transformValue is translate(100,200), then it will produce an array like ["translate(100,200)", "100"],
+ const match = transformValue.match(TRANSLATE_REGEXP);
+ if (match?.[1]) {
+ const xValue = match[1];
+ // Now find sibling g elements with the same x transform value
+ cy.get('.MuiCardContent-root')
+ .find('g')
+ .children('g')
+ .filter((_, element) => {
+ const siblingTransform = Cypress.$(element).attr('transform');
+ return siblingTransform?.includes(`translate(${xValue},`);
+ })
+ .should('have.length', 3); // Check that there's at least one element
+ } else {
+ throw new Error('Transform attribute not found or invalid format');
+ }
+ });
+ cy.get('.ndl-dropdown:contains("Group")').find('svg').parent().click().type('(none){enter}');
+ //Checking that the stacked grouped elements do not exist
+ cy.get('.MuiCardContent-root')
+ .find('g')
+ .children('g')
+ .eq(3) // Get the fourth g element (index starts from 0)
+ .invoke('attr', 'transform')
+ .then((transformValue) => {
+ // Captures the first number in the translate attribute using the parenthesis to capture the first digit and put it in the second value of the resulting array
+ // if transformValue is translate(100,200), then it will produce an array like ["translate(100,200)", "100"],
+ const match = transformValue.match(TRANSLATE_REGEXP);
+ if (match?.[1]) {
+ const xValue = match[1];
+ // Now find sibling g elements with the same x transform value
+ cy.get('.MuiCardContent-root')
+ .find('g')
+ .children('g')
+ .filter((_, element) => {
+ const siblingTransform = Cypress.$(element).attr('transform');
+ return siblingTransform?.includes(`translate(${xValue},`);
+ })
+ .should('have.length', 1); // Check that there are no matching elements
+ } else {
+ throw new Error('Transform attribute not found or invalid format');
+ }
+ });
+ });
+
+ // How to properly test this?
+ it.skip('Testing grouped grouping mode', () => {
+ page
+ .updateChartQuery(
+ 'MATCH (p:Person)-[:DIRECTED]->(n:Movie) RETURN n.released AS released, p.name AS Director, count(n.title) AS count LIMIT 5'
+ )
+ .updateDropdownAdvancedSetting('Grouping', 'on')
+ .updateDropdownAdvancedSetting('Group Mode', 'grouped');
+ cy.get('.ndl-dropdown:contains("Group")').find('svg').parent().click().type('Director{enter}');
+ });
+
+ it('Testing "Show Value on Bars"', () => {
+ page.updateDropdownAdvancedSetting('Show Values On Bars', 'on');
+ cy.get('.react-grid-layout:eq(0) .MuiGrid-root:eq(2)').find('div svg > g > g > text').should('have.length', 5);
+
+ page.updateDropdownAdvancedSetting('Show Values On Bars', 'off');
+ cy.get('.react-grid-layout:eq(0) .MuiGrid-root:eq(2)').find('div svg > g > g > text').should('not.exist');
+ });
+
+ describe('Y axis display', () => {
+ it('Checking Y axis is displayed', () => {
+ page.updateDropdownAdvancedSetting('Display Y axis', 'on');
+ cy.get('.MuiCardContent-root svg > g > g:nth-child(3)')
+ .invoke('attr', 'transform')
+ .should('eq', 'translate(0,0)');
+ });
+
+ it('Checking Y axis is hidden', () => {
+ page.updateDropdownAdvancedSetting('Display Y axis', 'off');
+ cy.get('.MuiCardContent-root svg > g > g:nth-child(3)')
+ .invoke('attr', 'transform')
+ .should('not.eq', 'translate(0,0)');
+ });
+ });
+
+ describe('Y grid lines display', () => {
+ it('Checking Y grid lines are displayed', () => {
+ page.updateDropdownAdvancedSetting('Display Y grid lines', 'on');
+ cy.get('.MuiCardContent-root svg g > g > line').invoke('attr', 'stroke').should('eq', '#dddddd');
+ });
+
+ it('Checking Y grid lines are hidden', () => {
+ page.updateDropdownAdvancedSetting('Display Y grid lines', 'off');
+ cy.get('.MuiCardContent-root svg g > g > line').invoke('attr', 'stroke').should('not.eq', '#dddddd');
+ });
+ });
+});
diff --git a/cypress/e2e/charts/table.cy.js b/cypress/e2e/charts/table.cy.js
new file mode 100644
index 000000000..5c086df1b
--- /dev/null
+++ b/cypress/e2e/charts/table.cy.js
@@ -0,0 +1,60 @@
+import { tableCypherQuery } from '../../fixtures/cypher_queries';
+import { Page } from '../../Page';
+
+const page = new Page();
+// Ignore warnings that may appear when using the Cypress dev server
+Cypress.on('uncaught:exception', (err, runnable) => {
+ console.log(err, runnable);
+ return false;
+});
+
+describe('Testing table', () => {
+ beforeEach('open neodash', () => {
+ page.init().createNewDashboard().connectToNeo4j();
+ cy.wait(100);
+ });
+
+ it.skip('create a table', () => {
+ //Opens the div containing all report cards
+ cy.get('.react-grid-layout:eq(0)')
+ .first()
+ .within(() => {
+ //Finds the 2nd card
+ cy.get('.MuiGrid-root')
+ .eq(1)
+ .within(() => {
+ //Clicks the 2nd button (opens settings)
+ cy.get('button').eq(1).click();
+ // cy.get('div[role="textbox"')
+ });
+ });
+ cy.get('.react-grid-layout')
+ .first()
+ .within(() => {
+ //Finds the 2nd card
+ cy.get('.MuiGrid-root')
+ .eq(1)
+ .within(() => {
+ //Opens the drop down
+ cy.getDataTest('type-dropdown').click();
+ });
+ });
+ // Selects the Table option
+ cy.get('[id^="react-select-5-option"]').contains(/Table/).should('be.visible').click({ force: true });
+ cy.get('.react-grid-layout .MuiGrid-root:eq(1) #type input[name="Type"]').should('have.value', 'Table');
+
+ //Removes text in cypher editor and types new query
+ cy.get('.react-grid-layout')
+ .first()
+ .within(() => {
+ //Finds the 2nd card
+ cy.get('.MuiGrid-root')
+ .eq(1)
+ .within(() => {
+ //Replaces default query with new query
+ cy.get('.ndl-cypher-editor div[role="textbox"]').clear().type(tableCypherQuery);
+ cy.get('button[aria-label="run"]').click();
+ });
+ });
+ });
+});
diff --git a/cypress/e2e/start_page.cy.js b/cypress/e2e/start_page.cy.js
index c1d2b5694..8038866b3 100644
--- a/cypress/e2e/start_page.cy.js
+++ b/cypress/e2e/start_page.cy.js
@@ -11,7 +11,11 @@ import {
formCypherQuery,
} from '../fixtures/cypher_queries';
-const WAITING_TIME = 20000;
+import { Page } from '../Page';
+
+const CARD_SELECTOR = 'main .react-grid-item:eq(2)';
+const page = new Page(CARD_SELECTOR);
+
// Ignore warnings that may appear when using the Cypress dev server
Cypress.on('uncaught:exception', (err, runnable) => {
console.log(err, runnable);
@@ -20,55 +24,22 @@ Cypress.on('uncaught:exception', (err, runnable) => {
describe('NeoDash E2E Tests', () => {
beforeEach(() => {
- cy.viewport(1920, 1080);
- // Navigate to index
- cy.visit('/', {
- onBeforeLoad(win) {
- win.localStorage.clear();
- },
- });
-
- cy.get('#form-dialog-title', { timeout: 20000 }).should('contain', 'NeoDash - Neo4j Dashboard Builder').click();
-
- cy.get('#form-dialog-title').then(($div) => {
- const text = $div.text();
- if (text == 'NeoDash - Neo4j Dashboard Builder') {
- cy.wait(500);
- // Create new dashboard
- cy.contains('New Dashboard').click();
- }
- });
-
- // If an old dashboard exists in cache, do a check to make sure we clear it.
- // if (cy.contains("Create new dashboard")) {
- // cy.contains('Yes').click()
- // }
-
- cy.get('#form-dialog-title', { timeout: 20000 }).should('contain', 'Connect to Neo4j');
-
- // Connect to Neo4j database
- // cy.get('#protocol').click()
- // cy.contains('neo4j').click()
- cy.get('#url').clear().type('localhost');
- // cy.get('#database').type('neo4j')
- cy.get('#dbusername').clear().type('neo4j');
- cy.get('#dbpassword').type('test1234');
- cy.get('button').contains('Connect').click();
+ page.init().createNewDashboard().connectToNeo4j();
cy.wait(100);
});
it('initializes the dashboard', () => {
- checkInitialState();
+ cy.checkInitialState();
});
it('creates a new card', () => {
- checkInitialState();
- createCard();
+ cy.checkInitialState();
+ cy.createCard();
});
// Test each type of card
it('creates a table report', () => {
- checkInitialState();
+ cy.checkInitialState();
cy.get('main .react-grid-item button[aria-label="add report"]').should('be.visible').click();
cy.get('main .react-grid-item')
.contains('No query specified.')
@@ -84,47 +55,38 @@ describe('NeoDash E2E Tests', () => {
cy.get('main .react-grid-item:eq(2)').contains('Advanced settings').click();
cy.get('main .react-grid-item:eq(2) button[aria-label="run"]').click();
- cy.get('main .react-grid-item:eq(2) .MuiDataGrid-columnHeaders', { timeout: WAITING_TIME })
+ cy.get('main .react-grid-item:eq(2) .MuiDataGrid-columnHeaders')
.should('contain', 'title')
.and('contain', 'released')
.and('not.contain', '__id');
- cy.get('main .react-grid-item:eq(2) .MuiDataGrid-virtualScroller .MuiDataGrid-row').should('have.length', 5);
- cy.get('main .react-grid-item:eq(2) .MuiDataGrid-footerContainer').should('contain', '1–5 of 8');
+ // cy.get('main .react-grid-item:eq(2) .MuiDataGrid-virtualScroller .MuiDataGrid-row').should('have.length', 5);
+ // cy.get('main .react-grid-item:eq(2) .MuiDataGrid-footerContainer').should('contain', '1–5 of 8');
cy.get('main .react-grid-item:eq(2) .MuiDataGrid-footerContainer button[aria-label="Go to next page"]').click();
cy.get('main .react-grid-item:eq(2) .MuiDataGrid-virtualScroller .MuiDataGrid-row').should('have.length', 3);
cy.get('main .react-grid-item:eq(2) .MuiDataGrid-footerContainer').should('contain', '6–8 of 8');
});
it('creates a bar chart report', () => {
- checkInitialState();
- createReportOfType('Bar Chart', barChartCypherQuery);
- cy.get('main .react-grid-item:eq(2) #index input[name="Category"]', { timeout: WAITING_TIME }).should(
- 'have.value',
- 'released'
- );
+ cy.checkInitialState();
+ page.createReportOfType('Bar Chart', barChartCypherQuery);
+ cy.get('main .react-grid-item:eq(2) #index input[name="Category"]').should('have.value', 'released');
cy.get('main .react-grid-item:eq(2) #value input[name="Value"]').should('have.value', 'count');
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g').should('have.length', 8);
});
it('creates a pie chart report', () => {
- checkInitialState();
- createReportOfType('Pie Chart', barChartCypherQuery);
- cy.get('main .react-grid-item:eq(2) #index input[name="Category"]', { timeout: WAITING_TIME }).should(
- 'have.value',
- 'released'
- );
+ cy.checkInitialState();
+ page.createReportOfType('Pie Chart', barChartCypherQuery);
+ cy.get('main .react-grid-item:eq(2) #index input[name="Category"]').should('have.value', 'released');
cy.get('main .react-grid-item:eq(2) #value input[name="Value"]').should('have.value', 'count');
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g').should('have.length', 3);
- cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g:nth-child(2) > path').should('have.length', 5);
+ cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g > path').should('have.length', 5);
});
it('creates a line chart report', () => {
- checkInitialState();
- createReportOfType('Line Chart', barChartCypherQuery);
- cy.get('main .react-grid-item:eq(2) #x input[name="X-value"]', { timeout: WAITING_TIME }).should(
- 'have.value',
- 'released'
- );
+ cy.checkInitialState();
+ page.createReportOfType('Line Chart', barChartCypherQuery);
+ cy.get('main .react-grid-item:eq(2) #x input[name="X-value"]').should('have.value', 'released');
cy.get('main .react-grid-item:eq(2) #value input[name="Y-value"]').should('have.value', 'count');
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g').should('have.length', 6);
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g:nth-child(2) > line').should(
@@ -134,83 +96,65 @@ describe('NeoDash E2E Tests', () => {
});
it('creates a map chart report', () => {
- checkInitialState();
- createReportOfType('Map', mapChartCypherQuery, true);
- cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > path', { timeout: WAITING_TIME }).should(
- 'have.length',
- 5
- );
+ cy.checkInitialState();
+ page.createReportOfType('Map', mapChartCypherQuery, true);
+ cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > path').should('have.length', 5);
});
it('creates a single value report', () => {
- checkInitialState();
- createReportOfType('Single Value', barChartCypherQuery);
- cy.get('main .react-grid-item:eq(2) .MuiCardContent-root > div > div:nth-child(2) > span', {
- timeout: WAITING_TIME,
- })
+ cy.checkInitialState();
+ page.createReportOfType('Single Value', barChartCypherQuery);
+ cy.get('main .react-grid-item:eq(2) .MuiCardContent-root > div > div:nth-child(2) > span')
.invoke('text')
.then((text) => {
expect(text).to.be.oneOf(['1999', '1,999', '1 999']);
});
});
- it('creates a gauge chart report', () => {
- enableAdvancedVisualizations();
- checkInitialState();
- createReportOfType('Gauge Chart', gaugeChartCypherQuery);
- cy.get('.text-group > text', { timeout: WAITING_TIME }).contains('69');
+ it.skip('creates a gauge chart report', () => {
+ page.enableAdvancedVisualizations();
+ cy.checkInitialState();
+ page.createReportOfType('Gauge Chart', gaugeChartCypherQuery);
+ cy.get('.text-group > text').contains('69');
});
it('creates a sunburst chart report', () => {
- enableAdvancedVisualizations();
- checkInitialState();
- createReportOfType('Sunburst Chart', sunburstChartCypherQuery);
- cy.get('main .react-grid-item:eq(2) #index input[name="Path"]', { timeout: WAITING_TIME }).should(
- 'have.value',
- 'x.path'
- );
+ page.enableAdvancedVisualizations();
+ cy.checkInitialState();
+ page.createReportOfType('Sunburst Chart', sunburstChartCypherQuery);
+ cy.get('main .react-grid-item:eq(2) #index input[name="Path"]').should('have.value', 'x.path');
cy.get('main .react-grid-item:eq(2) #value input[name="Value"]').should('have.value', 'x.value');
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g:nth-child(1) > path').should('have.length', 5);
});
it('creates a circle packing report', () => {
- enableAdvancedVisualizations();
- checkInitialState();
- createReportOfType('Circle Packing', sunburstChartCypherQuery);
- cy.get('main .react-grid-item:eq(2) #index input[name="Path"]', { timeout: WAITING_TIME }).should(
- 'have.value',
- 'x.path'
- );
+ page.enableAdvancedVisualizations();
+ cy.checkInitialState();
+ page.createReportOfType('Circle Packing', sunburstChartCypherQuery);
+ cy.get('main .react-grid-item:eq(2) #index input[name="Path"]').should('have.value', 'x.path');
cy.get('main .react-grid-item:eq(2) #value input[name="Value"]').should('have.value', 'x.value');
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > circle').should('have.length', 6);
});
it('creates a tree map report', () => {
- enableAdvancedVisualizations();
- checkInitialState();
- createReportOfType('Treemap', sunburstChartCypherQuery);
- cy.get('main .react-grid-item:eq(2) #index input[name="Path"]', { timeout: WAITING_TIME }).should(
- 'have.value',
- 'x.path'
- );
+ page.enableAdvancedVisualizations();
+ cy.checkInitialState();
+ page.createReportOfType('Treemap', sunburstChartCypherQuery);
+ cy.get('main .react-grid-item:eq(2) #index input[name="Path"]').should('have.value', 'x.path');
cy.get('main .react-grid-item:eq(2) #value input[name="Value"]').should('have.value', 'x.value');
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > g').should('have.length', 6);
});
it('creates a sankey chart report', () => {
- enableAdvancedVisualizations();
- checkInitialState();
- createReportOfType('Sankey Chart', sankeyChartCypherQuery, true);
- cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > path', { timeout: WAITING_TIME }).should(
- 'have.attr',
- 'fill-opacity',
- 0.5
- );
+ page.enableAdvancedVisualizations();
+ cy.checkInitialState();
+ page.createReportOfType('Sankey Chart', sankeyChartCypherQuery, true);
+ cy.get('main .react-grid-item:eq(2) .MuiCardContent-root svg > g > path').should('have.attr', 'fill-opacity', 0.5);
});
it('creates a raw json report', () => {
- checkInitialState();
- createReportOfType('Raw JSON', barChartCypherQuery);
+ cy.checkInitialState();
+ page.createReportOfType('Raw JSON', barChartCypherQuery);
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root textarea:nth-child(1)', { timeout: 45000 }).should(
($div) => {
const text = $div.text();
@@ -220,8 +164,8 @@ describe('NeoDash E2E Tests', () => {
});
it('creates a parameter select report', () => {
- checkInitialState();
- selectReportOfType('Parameter Select');
+ cy.checkInitialState();
+ page.selectReportOfType('Parameter Select');
cy.wait(500);
cy.get('#autocomplete-label-type').type('Movie');
cy.get('#autocomplete-label-type-option-0').click();
@@ -234,21 +178,21 @@ describe('NeoDash E2E Tests', () => {
});
it('creates an iframe report', () => {
- checkInitialState();
- createReportOfType('iFrame', iFrameText);
+ cy.checkInitialState();
+ page.createReportOfType('iFrame', iFrameText);
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root iframe', { timeout: 45000 }).should('be.visible');
});
it('creates a markdown report', () => {
- checkInitialState();
- createReportOfType('Markdown', markdownText);
+ cy.checkInitialState();
+ page.createReportOfType('Markdown', markdownText);
cy.get('main .react-grid-item:eq(2) .MuiCardContent-root h1', { timeout: 45000 }).should('have.text', 'Hello');
});
it.skip('creates a form report', () => {
- enableFormsExtension();
- checkInitialState();
- createReportOfType('Form', formCypherQuery, true, false);
+ page.enableFormsExtension();
+ cy.checkInitialState();
+ page.createReportOfType('Form', formCypherQuery, true, false);
cy.get('main .react-grid-item:eq(2) .form-add-parameter').click();
cy.wait(200);
cy.get('#autocomplete-label-type').type('Movie');
@@ -261,8 +205,10 @@ describe('NeoDash E2E Tests', () => {
cy.get('main .react-grid-item:eq(2) button[aria-label="run"]').scrollIntoView().should('be.visible').click();
cy.wait(500);
+ cy.get('#form-submit').should('be.disabled');
cy.get('#autocomplete').type('The Matrix');
cy.get('#autocomplete-option-0').click();
+ cy.get('#form-submit').should('not.be.disabled');
cy.get('#form-submit').click();
cy.wait(500);
cy.get('.form-submitted-message').should('have.text', 'Form Submitted.Reset Form');
@@ -272,7 +218,7 @@ describe('NeoDash E2E Tests', () => {
// TODO - this test is flaky, especially in GitHub actions environment.
it.skip('test load dashboard from file and stress test report customizations', () => {
try {
- var NUMBER_OF_PAGES_IN_STRESS_TEST_DASHBOARD = 5;
+ const NUMBER_OF_PAGES_IN_STRESS_TEST_DASHBOARD = 5;
const file = cy.request(loadDashboardURL).should((response) => {
cy.get('#root .MuiDrawer-root .MuiIconButton-root:eq(2)').click();
cy.get('.MuiDialog-root .MuiPaper-root .MuiDialogContent-root textarea:eq(0)')
@@ -293,62 +239,3 @@ describe('NeoDash E2E Tests', () => {
}
});
});
-
-function enableAdvancedVisualizations() {
- cy.get('main button[aria-label="Extensions').should('be.visible').click();
- cy.get('#checkbox-advanced-charts').should('be.visible').click();
- cy.get('.ndl-dialog-close').scrollIntoView().should('be.visible').click();
- cy.wait(200);
-}
-
-function enableFormsExtension() {
- cy.get('main button[aria-label="Extensions').should('be.visible').click();
- cy.get('#checkbox-forms').scrollIntoView();
- cy.get('#checkbox-forms').should('be.visible').click();
- cy.get('.ndl-dialog-close').scrollIntoView().should('be.visible').click();
- cy.wait(200);
-}
-
-function selectReportOfType(type) {
- cy.get('main .react-grid-item button[aria-label="add report"]').should('be.visible').click();
- cy.get('main .react-grid-item')
- .contains('No query specified.')
- .parentsUntil('.react-grid-item')
- .find('button[aria-label="settings"]', { timeout: 2000 })
- .should('be.visible')
- .click();
- cy.get('main .react-grid-item:eq(2) #type', { timeout: 2000 }).should('be.visible').click();
- cy.contains(type).click();
- cy.wait(100);
-}
-
-function createReportOfType(type, query, fast = false, run = true) {
- selectReportOfType(type);
- if (fast) {
- cy.get('main .react-grid-item:eq(2) .ReactCodeMirror').type(query, { delay: 1, parseSpecialCharSequences: false });
- } else {
- cy.get('main .react-grid-item:eq(2) .ReactCodeMirror').type(query, { parseSpecialCharSequences: false });
- }
- cy.wait(400);
-
- cy.get('main .react-grid-item:eq(2)').contains('Advanced settings').click();
- if (run) {
- cy.get('main .react-grid-item:eq(2) button[aria-label="run"]').click();
- }
-}
-
-function checkInitialState() {
- // Check the starter cards
- cy.get('main .react-grid-item:eq(0)').should('contain', 'This is your first dashboard!');
- cy.get('main .react-grid-item:eq(1) .force-graph-container canvas').should('be.visible');
- cy.get('main .react-grid-item:eq(2) button').should('have.attr', 'aria-label', 'add report');
-}
-
-function createCard() {
- // Check the starter cards
- cy.get('main .react-grid-item button[aria-label="add report"]', { timeout: WAITING_TIME })
- .should('be.visible')
- .click();
- cy.wait(1000);
- cy.get('main .react-grid-item:eq(2)').should('contain', 'No query specified.');
-}
diff --git a/cypress/fixtures/cypher_queries.js b/cypress/fixtures/cypher_queries.js
index 4f009b7ae..c47c6c2be 100644
--- a/cypress/fixtures/cypher_queries.js
+++ b/cypress/fixtures/cypher_queries.js
@@ -1,3 +1,4 @@
+// Cypher queries - for component testing
export const defaultCypherQuery = 'MATCH (n) RETURN n LIMIT 25';
export const tableCypherQuery =
'MATCH (n:Movie) RETURN n.title AS title, n.released AS released, id(n) AS __id LIMIT 8';
@@ -13,6 +14,13 @@ export const sankeyChartCypherQuery =
"WITH [ { path: { start: {labels: ['Person'], identity: 1, properties: {name: 'Jim'}}, end: {identity: 11}, length: 1, segments: [ { start: {labels: ['Person'], identity: 1, properties: {name: 'Jim'}}, relationship: {type: 'RATES', start: 1, end: 11, identity: 10001, properties: {value: 4.5}}, end: {labels: ['Movie'], identity: 11,properties: {title: 'The Matrix', released: 1999}} } ] }, person: 'Jim', movie: 'The Matrix', value: 4.5 }, { path: { start: {labels: ['Person'], identity: 2, properties: {name: 'Mike'}}, end: {identity: 11}, length: 1, segments: [ { start: {labels: ['Person'], identity: 2, properties: {name: 'Mike'}}, relationship: {type: 'RATES', start: 2, end: 11, identity: 10002, properties: {value: 3.8}}, end: {labels: ['Movie'], identity: 11,properties: {title: 'The Matrix', released: 1999}} } ] }, person: 'Mike', movie: 'The Matrix', value: 3.8 } ] as data UNWIND data as row RETURN row.path as Path";
export const gaugeChartCypherQuery = 'RETURN 69';
export const formCypherQuery = 'MATCH (n:Movie) WHERE n.title = $neodash_movie_title SET n.rating = 92';
+
+// Cypher queries - for renderer testing
+export const stringArrayCypherQuery = "RETURN ['initial', 'list'] AS column, ['other', 'list'] AS otherColumn";
+export const intArrayCypherQuery = 'RETURN [1, 2] AS column, [3, 4] AS otherColumn';
+export const pathArrayCypherQuery = 'MATCH p=(:Person)-[:ACTED_IN]->(:Movie) WITH p LIMIT 1 RETURN p';
+
+// Other content fixtures
export const iFrameText = 'https://www.wikipedia.org/';
export const markdownText = '# Hello';
export const loadDashboardURL =
diff --git a/cypress/support/index.js b/cypress/index.js
similarity index 95%
rename from cypress/support/index.js
rename to cypress/index.js
index 37a498fb5..cbfe0a766 100644
--- a/cypress/support/index.js
+++ b/cypress/index.js
@@ -14,7 +14,7 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
-import './commands';
+import './support/commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 119ab03f7..c7e9831e0 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -23,3 +23,30 @@
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
+Cypress.Commands.add('getDataTest', (dataTestSelector) => {
+ return cy.get(`[data-test="${dataTestSelector}"]`);
+});
+
+/**
+ * Function to interact with a specific element and execute additional custom commands.
+ * @param {Function} customAction - A callback function containing custom Cypress commands.
+ */
+
+//Used in start_page.cy.js
+Cypress.Commands.add('checkInitialState', () => {
+ // Check the starter cards
+ cy.get('main .react-grid-item:eq(0)').should('contain', 'This is your first dashboard!');
+ cy.get('main .react-grid-item:eq(1) .force-graph-container canvas').should('be.visible');
+ cy.get('main .react-grid-item:eq(2) button').should('have.attr', 'aria-label', 'add report');
+});
+
+// Creates a card
+const WAITING_TIME = 20000;
+Cypress.Commands.add('createCard', () => {
+ // Check the starter cards
+ cy.get('main .react-grid-item button[aria-label="add report"]', { timeout: WAITING_TIME })
+ .should('be.visible')
+ .click();
+ cy.wait(1000);
+ cy.get('main .react-grid-item:eq(2)').should('contain', 'No query specified.');
+});
diff --git a/docs/modules/ROOT/images/dashboardaccesscontrol.png b/docs/modules/ROOT/images/dashboardaccesscontrol.png
new file mode 100644
index 000000000..3ca825baf
Binary files /dev/null and b/docs/modules/ROOT/images/dashboardaccesscontrol.png differ
diff --git a/docs/modules/ROOT/images/dashboardnew.png b/docs/modules/ROOT/images/dashboardnew.png
new file mode 100644
index 000000000..203e26d61
Binary files /dev/null and b/docs/modules/ROOT/images/dashboardnew.png differ
diff --git a/docs/modules/ROOT/images/dashboardnewsettings.png b/docs/modules/ROOT/images/dashboardnewsettings.png
new file mode 100644
index 000000000..ec1ccf887
Binary files /dev/null and b/docs/modules/ROOT/images/dashboardnewsettings.png differ
diff --git a/docs/modules/ROOT/images/rolelabelmodal.png b/docs/modules/ROOT/images/rolelabelmodal.png
new file mode 100644
index 000000000..804afb17a
Binary files /dev/null and b/docs/modules/ROOT/images/rolelabelmodal.png differ
diff --git a/docs/modules/ROOT/images/rolesmenu.png b/docs/modules/ROOT/images/rolesmenu.png
new file mode 100644
index 000000000..4f948ba22
Binary files /dev/null and b/docs/modules/ROOT/images/rolesmenu.png differ
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 0fda190cf..f6984cda2 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -34,6 +34,7 @@
*** xref:user-guide/extensions/report-actions.adoc[Report Actions]
*** xref:user-guide/extensions/natural-language-queries.adoc[Text2Cypher - Natural Language Queries]
*** xref:user-guide/extensions/forms.adoc[Forms]
+*** xref:user-guide/extensions/access-control-management.adoc[Access Control Management]
** xref:user-guide/faq.adoc[FAQ]
* xref:developer-guide/index.adoc[Developer Guide]
** xref:developer-guide/build-and-run.adoc[Build & Run]
diff --git a/docs/modules/ROOT/pages/banner.adoc b/docs/modules/ROOT/pages/banner.adoc
new file mode 100644
index 000000000..fdb12c826
--- /dev/null
+++ b/docs/modules/ROOT/pages/banner.adoc
@@ -0,0 +1,6 @@
+[NOTE]
+====
+This documentation pertains to the unsupported version of NeoDash, as part of Neo4j Labs.
+For users of the supported NeoDash offering, refer to https://neo4j.com/docs/neodash-commercial/[NeoDash commercial].
+
+====
\ No newline at end of file
diff --git a/docs/modules/ROOT/pages/developer-guide/adding-visualizations.adoc b/docs/modules/ROOT/pages/developer-guide/adding-visualizations.adoc
index dc47ee4ab..4d9fd76eb 100644
--- a/docs/modules/ROOT/pages/developer-guide/adding-visualizations.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/adding-visualizations.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Adding Visualizations
You can extend NeoDash with your own visualizations without diving deep
diff --git a/docs/modules/ROOT/pages/developer-guide/build-and-run.adoc b/docs/modules/ROOT/pages/developer-guide/build-and-run.adoc
index 45cbc2641..f52b88fb5 100644
--- a/docs/modules/ROOT/pages/developer-guide/build-and-run.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/build-and-run.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Build & Run
To start developing the application, you will need to set up the
@@ -76,7 +78,9 @@ docker run -it –rm -p 5005:5005 neodash
== Run on Kubernetes
-An example of a pod definition YAML file to create a NeoDash pod in a cluster:
+=== To deploy using YAML files
+
+YAML examples are available in the https://github.com/neo4j-labs/neodash[NeoDash repository]. Here is an example of a pod definition YAML file to create a NeoDash pod in a cluster:
....
apiVersion: v1
@@ -108,3 +112,118 @@ spec:
selector:
project: neodash
....
+
+=== To deploy using a Helm Charts
+
+A Kubernetes Helm chart is available in the https://github.com/neo4j-labs/neodash[the NeoDash repository] and here is the full example of the Helm chart values.yaml file,
+
+....
+# Name override or full name override
+nameOverride: ''
+fullnameOverride: neodash-test
+
+# Number of pods
+replicaCount: 1
+
+# Image Details
+image:
+ repository: neo4jlabs/neodash
+ pullPolicy: IfNotPresent
+ tag: 'latest'
+imagePullSecrets: [] # Image pull secret if any
+
+# Pod annotations, labels and security context
+podAnnotations: {}
+podLabels: {}
+podSecurityContext: {}
+
+# Mode configuration using environment variables
+# Set reader mode environment variables when enable_reader_mode is true
+enable_reader_mode: true
+env:
+ - name: "ssoEnabled"
+ value: "false"
+ - name: "standalone"
+ value: "true"
+ - name: "standaloneProtocol"
+ value: "neo4j+s"
+ - name: "standaloneHost"
+ value: "localhost"
+ - name: "standalonePort"
+ value: "7687"
+ - name: "standaloneDatabase"
+ value: neo4j
+ - name: "standaloneDashboardName"
+ value: "test"
+ - name: "standaloneDashboardDatabase"
+ value: neo4j
+ - name: "standaloneAllowLoad"
+ value: "false"
+ - name: "standaloneLoadFromOtherDatabases"
+ value: "false"
+ - name: "standaloneMultiDatabase"
+ value: "false"
+
+# Environment variable from secret
+envFromSecrets: []
+ # standaloneUsername:
+ # secretName: "neo4j-connection-secrets"
+ # key: "username"
+ # standalonePassword:
+ # secretName: "neo4j-connection-secrets"
+ # key: "password"
+
+# Service details
+service:
+ type: LoadBalancer # Can also be ClusterIP or NodePort
+ port: 5005 # For the service to listen in for Traffic
+ targetPort: 5005 # Target port is the container port
+ annotations: {} # Service annotations for the LoadBalance
+
+# Ingress
+ingress:
+ enabled: false # Enable Kubernetes Ingress
+ className: 'alb' # Class Name
+ annotations: {} # Cloud LoadBalancer annotations
+ hosts: []
+ # - host: neodash.example.com
+ # paths:
+ # - path: '/'
+ # pathType: Prefix
+ tls: []
+
+# Pod resources request, limits and health check
+resources:
+ requests:
+ memory: "64Mi"
+ cpu: "250m"
+ limits:
+ memory: "128Mi"
+ cpu: "500m"
+livenessProbe:
+ httpGet:
+ path: /*
+ port: 5005
+readinessProbe:
+ httpGet:
+ path: /*
+ port: 5005
+
+# Pod Autoscaler
+autoscaling:
+ enabled: false
+ # minReplicas: 1
+ # maxReplicas: 100
+ # targetCPUUtilizationPercentage: 80
+
+# Pod Volumes
+volumes: []
+volumeMounts: []
+
+# Service Account
+serviceAccount:
+ create: true
+ automount: true
+ # annotations: {}
+ # name: ''
+....
\ No newline at end of file
diff --git a/docs/modules/ROOT/pages/developer-guide/component-overview.adoc b/docs/modules/ROOT/pages/developer-guide/component-overview.adoc
index e02da7989..27d78236d 100644
--- a/docs/modules/ROOT/pages/developer-guide/component-overview.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/component-overview.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Component Overview
diff --git a/docs/modules/ROOT/pages/developer-guide/configuration.adoc b/docs/modules/ROOT/pages/developer-guide/configuration.adoc
index 355c0c19a..cca62fdbd 100644
--- a/docs/modules/ROOT/pages/developer-guide/configuration.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/configuration.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Configuration
When using a custom NeoDash deployment, there are several settings that
@@ -42,7 +44,7 @@ will look like this:
|===
|Name |Type |Default Value |Description
|ssoEnabled |boolean |false |If enabled, lets users connect to Neo4j
-using SSO. This requires the app to be running in standalone mode, and a
+using SSO. This requires a
valid ssoDiscoveryUrl to be set.
|ssoProviders |List |[] |When using multiple SSO providers on the database, you can configure the list of providers (by id) to be used on Neodash. If empty, all providers will be displayed.
@@ -133,7 +135,7 @@ must be granted to enble any user to create logs.
⚠️ * Load/Save from/to file are not logged (only from/to Database)
-|loggingDatabase |string |neo4j |When loggingMode is set to anything
+|loggingDatabase |string |logs |When loggingMode is set to anything
else than '0', the database to use for logging. Log records (nodes)
will be created in this database.
diff --git a/docs/modules/ROOT/pages/developer-guide/contributing.adoc b/docs/modules/ROOT/pages/developer-guide/contributing.adoc
index 1cb695dde..90c198ff9 100644
--- a/docs/modules/ROOT/pages/developer-guide/contributing.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/contributing.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Contributing
Contributions to the project are highly welcomed. Please consider
diff --git a/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc b/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc
index 874366f70..5646b89ad 100644
--- a/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/deploy-a-build.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Deploy a Build
If you have a pre-built NeoDash application, you can easily deploy it on an any webserver.
A NeoDash build is "just" a collection of HTML, CSS and JavaScript files, so it can run virtually anywhere.
@@ -37,7 +39,7 @@ Depending on the webserver type and version, this could be different directory.
As an example - to copy the files to an nginx webserver using `scp`:
```bash
-scp neodash-2.4.2 username@host:/usr/share/nginx/html
+scp neodash-2.4.9-labs username@host:/usr/share/nginx/html
```
NeoDash should now be visible by visiting your (sub)domain in the browser.
diff --git a/docs/modules/ROOT/pages/developer-guide/design.adoc b/docs/modules/ROOT/pages/developer-guide/design.adoc
index 5f498bb2d..3c7bd32eb 100644
--- a/docs/modules/ROOT/pages/developer-guide/design.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/design.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Design
This page contains some key guidelines for design of the application.
diff --git a/docs/modules/ROOT/pages/developer-guide/index.adoc b/docs/modules/ROOT/pages/developer-guide/index.adoc
index f4538d764..b9424ebb0 100644
--- a/docs/modules/ROOT/pages/developer-guide/index.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/index.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Developer Guide
This guide contains information for developers looking to deploy NeoDash, or extend it for their own needs.
diff --git a/docs/modules/ROOT/pages/developer-guide/session-storage.adoc b/docs/modules/ROOT/pages/developer-guide/session-storage.adoc
index a4861a4cf..7a668e4a7 100644
--- a/docs/modules/ROOT/pages/developer-guide/session-storage.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/session-storage.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Session Storage
This reducer serves only to store data that we want to reset at each new session.
diff --git a/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc b/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc
index f752cbad1..58575256e 100644
--- a/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Standalone Mode
Next to being a dashboard editor, NeoDash can be deployed in a
diff --git a/docs/modules/ROOT/pages/developer-guide/state-management.adoc b/docs/modules/ROOT/pages/developer-guide/state-management.adoc
index bdece9b9e..1083bf2c1 100644
--- a/docs/modules/ROOT/pages/developer-guide/state-management.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/state-management.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= State Management
NeoDash is an application with a complex internal state. If you are
diff --git a/docs/modules/ROOT/pages/developer-guide/style-configuration.adoc b/docs/modules/ROOT/pages/developer-guide/style-configuration.adoc
index e9d5af5c4..4d5495a91 100644
--- a/docs/modules/ROOT/pages/developer-guide/style-configuration.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/style-configuration.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Style Configuration
When using a custom NeoDash deployment, there are several theme variables that
@@ -6,8 +8,14 @@ link:https://cdn.jsdelivr.net/npm/@neo4j-ndl/base@1.4.0/lib/tokens/css/tokens.cs
For a simple (non-Dockerized) deployment, these configuration parameters
can be changed by modifying `dist/style.config.json` after you have built the
-application. When Docker image, these can not be passed as environment
-variables.
+application. When using the NeoDash Docker image, these can be passed as environment
+variables. For example:
+
+....
+docker run -p 5005:5005 \
+ -e DASHBOARD_HEADER_BRAND_LOGO=https://picsum.photos/500/100 \
+ neo4jlabs/neodash
+....
An example configuration for NeoDash
diff --git a/docs/modules/ROOT/pages/developer-guide/testing.adoc b/docs/modules/ROOT/pages/developer-guide/testing.adoc
index 402ed1bca..5c492a375 100644
--- a/docs/modules/ROOT/pages/developer-guide/testing.adoc
+++ b/docs/modules/ROOT/pages/developer-guide/testing.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Testing
NeoDash uses *Cypress* for automated testing. To install Cypress, check
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc
index 6c7816dce..83eadebd0 100644
--- a/docs/modules/ROOT/pages/index.adoc
+++ b/docs/modules/ROOT/pages/index.adoc
@@ -1,3 +1,5 @@
+include::/banner.adoc[]
+
= Introduction
This portal contains information on getting started with NeoDash - A Low-Code Dashboard Builder for Neo4j.
diff --git a/docs/modules/ROOT/pages/quickstart.adoc b/docs/modules/ROOT/pages/quickstart.adoc
index fea4b67ad..4ae165058 100644
--- a/docs/modules/ROOT/pages/quickstart.adoc
+++ b/docs/modules/ROOT/pages/quickstart.adoc
@@ -1,3 +1,5 @@
+include::/banner.adoc[]
+
= Quickstart
There are three easy ways to run NeoDash and start dashboarding your Neo4j data:
@@ -21,7 +23,7 @@ yarn run dev
NeoDash connects to any recent version of the Neo4j database. (Neo4j 4.0
or later). The quickest way to get started is to create a free cloud
-database on https://neo4j.io[Neo4j Aura].
+database on https://console.neo4j.io[Neo4j Aura].
To get started with building your own dashboard, see the Dashboards
page.
diff --git a/docs/modules/ROOT/pages/user-guide/access-control.adoc b/docs/modules/ROOT/pages/user-guide/access-control.adoc
new file mode 100644
index 000000000..47cf3da31
--- /dev/null
+++ b/docs/modules/ROOT/pages/user-guide/access-control.adoc
@@ -0,0 +1,11 @@
+include::../banner.adoc[]
+
+= Access Control
+
+The Access Control feature in NeoDash is a security measure that allows Users with write access or higher privileges to manage who has access to specific dashboards.
+
+
+== How it Works
+
+Navigate to a specific dashboard and inside the dashboard settings click on the 'Access Control' option in the dashboard sidebar. This opens a modal where users can add labels to the dashboard. These labels are then used to determine which users have access to the dashboard. Please keep in mind that prior to doing this, an administrator needs to provide certain privileges for different user roles for each label in order for this to work. You can read more about how RBAC works in Neo4j by reading the [Neo4j RBAC documentation](https://neo4j.com/docs/operations-manual/current/authentication-authorization/manage-privileges/).
+
diff --git a/docs/modules/ROOT/pages/user-guide/bloom-integration.adoc b/docs/modules/ROOT/pages/user-guide/bloom-integration.adoc
index 8acdc4e92..09dcce58c 100644
--- a/docs/modules/ROOT/pages/user-guide/bloom-integration.adoc
+++ b/docs/modules/ROOT/pages/user-guide/bloom-integration.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Bloom Integration
NeoDash can be linked to Neo4j Bloom perspectives by using
diff --git a/docs/modules/ROOT/pages/user-guide/dashboards.adoc b/docs/modules/ROOT/pages/user-guide/dashboards.adoc
index 5392f11eb..d0aa53b11 100644
--- a/docs/modules/ROOT/pages/user-guide/dashboards.adoc
+++ b/docs/modules/ROOT/pages/user-guide/dashboards.adoc
@@ -1,9 +1,11 @@
+include::../banner.adoc[]
+
= Dashboards
In NeoDash, a dashboard consists of several pages, each of which can
consist of multiple reports.
-image::dashboard2.png[Dashboard]
+image::dashboardnew.png[Dashboard]
As an example: The screenshot above shows a dashboard with three pages:
`Breweries`, `Beer Ratings` and `Styles`. The dashboard title `My
@@ -21,7 +23,7 @@ dashboard or open an existing one (if available). After being connected,
the buttons on the sidebar can be used to save, load or share a
dashboard.
-image::saveloadshare.png[Save/Load/Share Button]
+image::dashboardnewsettings.png[Save/Load/Share Button]
=== Save a Dashboard
@@ -115,6 +117,15 @@ When creating a NeoDash deployment on a production database, it is not
recommended to use the `Share' feature. Rather, set up a dedicated
standalone deployment of NeoDash. See Publishing for more infomation.
+=== Dashboard Access Control
+With this feature, you can manage dashboard access by leveraging the native Neo4j Role-based Access Control (RBAC) functionality. Attach additional labels to the currently selected dashboard node within this window, either by utilizing existing labels in your database or creating new ones, to regulate access permissions.
+
+You can find the Dashboard Access Control feature by clicking on the three dots next to the dashboard name in the sidebar and selecting the "Access Control" option.
+
+> This approach should be used together with restricted privileges on labels, assigned to certain roles. See link:../extensions/access-control-management[Access Control Management] for details.
+
+image::dashboardaccesscontrol.png[Dashboard Access Control]
+
== Dashboard Settings
Settings for the entire dashboard can be accessed by clicking the
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/access-control-management.adoc b/docs/modules/ROOT/pages/user-guide/extensions/access-control-management.adoc
new file mode 100644
index 000000000..9a6219c45
--- /dev/null
+++ b/docs/modules/ROOT/pages/user-guide/extensions/access-control-management.adoc
@@ -0,0 +1,28 @@
+include::../../banner.adoc[]
+
+= Access Control Management
+
+This extension lets you manage access control for roles and users, letting you assign users to roles as well as controlling which node labels can be read by a user.
+
+This extension is only visible to users with the role of "Administrator" or "Super User". Enabling this extension will allow the admin user to manage the labels of the roles in the database and then attach them to the users.
+
+
+== Using the Extension ==
+If you have logged in to Neodash as an admin user, you will be able to enable the extension in the "Extensions" menu. Clicking on this extension will give the user a new button next to the settings button in the dashboard header. If the user click on this button, a menu will appear with all the roles in the database.
+
+image::rolesmenu.png[Role menu]
+
+The user can then click on any role and a window will appear with the role's context:
+
+* User list - This is a list of users from your database. You can select multiple users from the list and the role will be added to all the selected users.
+
+* Allow list - This is a list of labels that the role will be granted to read. You can select multiple labels from the list or if you want every label to be granted, you can select "*" from the list. (Requires a database to be selected)
+
+* Deny list - This is a list of labels that the role will be denied to read. You can select multiple labels from the list or if you want every label to be denied, you can select "*" from the list. (Requires a database to be selected)
+
+
+Finally when the admin user clicks on the "Save" button, the role will be updated in the database and the labels will be granted or denied to the users that were selected for the specific role and database.
+
+image::rolelabelmodal.png[Role modal]
+
+> Universal (Cross-database) `GRANT` and `DENY` privileges are not supported by this extension. Privileges must be added on a database-specific level. See the Neo4j https://neo4j.com/docs/operations-manual/current/authentication-authorization/privileges-reads/[documentation on read privileges] for more information.
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/advanced-visualizations.adoc b/docs/modules/ROOT/pages/user-guide/extensions/advanced-visualizations.adoc
index fb02c5a94..f4e7e8e98 100644
--- a/docs/modules/ROOT/pages/user-guide/extensions/advanced-visualizations.adoc
+++ b/docs/modules/ROOT/pages/user-guide/extensions/advanced-visualizations.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Advanced Visualizations
Advanced visualizations let you extend your dashboard with complex, powerful visualizations beyond the standard visualizations.
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/forms.adoc b/docs/modules/ROOT/pages/user-guide/extensions/forms.adoc
index 4bce11d4d..3ea8c965e 100644
--- a/docs/modules/ROOT/pages/user-guide/extensions/forms.adoc
+++ b/docs/modules/ROOT/pages/user-guide/extensions/forms.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Forms
The 'forms' extension lets you combine different parameter selectors to update / modify your graph data.
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/index.adoc b/docs/modules/ROOT/pages/user-guide/extensions/index.adoc
index dc893b08b..6d16b1700 100644
--- a/docs/modules/ROOT/pages/user-guide/extensions/index.adoc
+++ b/docs/modules/ROOT/pages/user-guide/extensions/index.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Extensions
Extensions provide a way to expand the basic functionality of NeoDash with extra features.
@@ -19,6 +21,7 @@ The currently available extensions in NeoDash are:
- link:report-actions[Report Actions]
- link:natural-language-queries[Text2Cypher - Natural Language Queries]
- link:forms[Forms]
+- link:access-control-management[Access Control Management]
== Types of Extensions
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/natural-language-queries.adoc b/docs/modules/ROOT/pages/user-guide/extensions/natural-language-queries.adoc
index e7f9ea059..675a00a2d 100644
--- a/docs/modules/ROOT/pages/user-guide/extensions/natural-language-queries.adoc
+++ b/docs/modules/ROOT/pages/user-guide/extensions/natural-language-queries.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Text2Cypher - Natural Language Queries
Use natural language to generate Cypher queries in NeoDash. Connect to an LLM through an API, and let NeoDash use your database schema + the report types to generate queries automatically.
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/report-actions.adoc b/docs/modules/ROOT/pages/user-guide/extensions/report-actions.adoc
index 8b50f1651..a381c0076 100644
--- a/docs/modules/ROOT/pages/user-guide/extensions/report-actions.adoc
+++ b/docs/modules/ROOT/pages/user-guide/extensions/report-actions.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Report Actions
link:../#_2_pro_extensions[label:Pro Extension[]]
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/rule-based-styling.adoc b/docs/modules/ROOT/pages/user-guide/extensions/rule-based-styling.adoc
index ee6adc4ca..8a4b0ea9c 100644
--- a/docs/modules/ROOT/pages/user-guide/extensions/rule-based-styling.adoc
+++ b/docs/modules/ROOT/pages/user-guide/extensions/rule-based-styling.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Rule-Based Styling
diff --git a/docs/modules/ROOT/pages/user-guide/extensions/workflows.adoc b/docs/modules/ROOT/pages/user-guide/extensions/workflows.adoc
index 2b95b06d5..a735dbc2c 100644
--- a/docs/modules/ROOT/pages/user-guide/extensions/workflows.adoc
+++ b/docs/modules/ROOT/pages/user-guide/extensions/workflows.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Workflows
Introducing an advanced extension for creating, managing, and running workflows with Cypher queries. Simplify ETL flows, execute complex query chains, and run graph data science workloads effortlessly from Neodash.
diff --git a/docs/modules/ROOT/pages/user-guide/faq.adoc b/docs/modules/ROOT/pages/user-guide/faq.adoc
index cdd5efaad..08ece7dd1 100644
--- a/docs/modules/ROOT/pages/user-guide/faq.adoc
+++ b/docs/modules/ROOT/pages/user-guide/faq.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= FAQ
== 1. How can I learn more about NeoDash?
diff --git a/docs/modules/ROOT/pages/user-guide/index.adoc b/docs/modules/ROOT/pages/user-guide/index.adoc
index dde9d9e05..d87dffb6d 100644
--- a/docs/modules/ROOT/pages/user-guide/index.adoc
+++ b/docs/modules/ROOT/pages/user-guide/index.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= User Guide
The following pages contain everything you need to get started with NeoDash.
diff --git a/docs/modules/ROOT/pages/user-guide/pages.adoc b/docs/modules/ROOT/pages/user-guide/pages.adoc
index b332e4ae7..0d01f37d4 100644
--- a/docs/modules/ROOT/pages/user-guide/pages.adoc
+++ b/docs/modules/ROOT/pages/user-guide/pages.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Pages
A page is a collection of link:../reports[reports] that can be viewed at
diff --git a/docs/modules/ROOT/pages/user-guide/publishing.adoc b/docs/modules/ROOT/pages/user-guide/publishing.adoc
index 2b1015a03..8fe77739c 100644
--- a/docs/modules/ROOT/pages/user-guide/publishing.adoc
+++ b/docs/modules/ROOT/pages/user-guide/publishing.adoc
@@ -1,3 +1,5 @@
+include::../banner.adoc[]
+
= Publishing
When you are done building a dashboard, you may want to *publish* that
diff --git a/docs/modules/ROOT/pages/user-guide/reports/areamap.adoc b/docs/modules/ROOT/pages/user-guide/reports/areamap.adoc
index d28f4c6bc..9d3c4a79e 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/areamap.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/areamap.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Area Map
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc b/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc
index 56a8773c1..6a280c750 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Bar Chart
A bar chart will draw categories and values in a familiar bar-layout.
diff --git a/docs/modules/ROOT/pages/user-guide/reports/choropleth.adoc b/docs/modules/ROOT/pages/user-guide/reports/choropleth.adoc
index 35fdbdd19..8cfd6037c 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/choropleth.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/choropleth.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Choropleth
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/circle-packing.adoc b/docs/modules/ROOT/pages/user-guide/reports/circle-packing.adoc
index 61146b24d..cab5079bd 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/circle-packing.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/circle-packing.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Circle Packing
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/form.adoc b/docs/modules/ROOT/pages/user-guide/reports/form.adoc
index d7817f171..f10776561 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/form.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/form.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Form
A form is a special type of report that lets users run predefined, parameterized queries.
diff --git a/docs/modules/ROOT/pages/user-guide/reports/gantt.adoc b/docs/modules/ROOT/pages/user-guide/reports/gantt.adoc
index c54567aa5..0332fdd04 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/gantt.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/gantt.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Gantt Chart
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/gauge-chart.adoc b/docs/modules/ROOT/pages/user-guide/reports/gauge-chart.adoc
index 067acd6aa..41ed80eba 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/gauge-chart.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/gauge-chart.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Gauge Chart
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/graph.adoc b/docs/modules/ROOT/pages/user-guide/reports/graph.adoc
index 4d18523dd..8638ac9e6 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/graph.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/graph.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Graph
The graph report will render all returned nodes, relationships and paths
diff --git a/docs/modules/ROOT/pages/user-guide/reports/graph3d.adoc b/docs/modules/ROOT/pages/user-guide/reports/graph3d.adoc
index 31d2ef6a2..d2ddeb9dc 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/graph3d.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/graph3d.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= 3D Graph
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/iframe.adoc b/docs/modules/ROOT/pages/user-guide/reports/iframe.adoc
index 44e822e08..8f0fb9598 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/iframe.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/iframe.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= iFrame
An iFrame report lets you embed a webpage inside your NeoDash dashboard.
diff --git a/docs/modules/ROOT/pages/user-guide/reports/index.adoc b/docs/modules/ROOT/pages/user-guide/reports/index.adoc
index da44ed606..d05324060 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/index.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/index.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Reports
A report is the smallest building build of your dashboard. Each report
diff --git a/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc b/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc
index ec0c21d52..56adcd965 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Line Chart
A line chart can be used to draw one or more lines in a two-dimensional
diff --git a/docs/modules/ROOT/pages/user-guide/reports/map.adoc b/docs/modules/ROOT/pages/user-guide/reports/map.adoc
index 081b917f2..4779c3c98 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/map.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/map.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Map
The map report will render all returned nodes, relationships and paths
diff --git a/docs/modules/ROOT/pages/user-guide/reports/markdown.adoc b/docs/modules/ROOT/pages/user-guide/reports/markdown.adoc
index fc09c3726..85d19cec6 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/markdown.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/markdown.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Markdown
Markdown reports let you specify
diff --git a/docs/modules/ROOT/pages/user-guide/reports/parameter-select.adoc b/docs/modules/ROOT/pages/user-guide/reports/parameter-select.adoc
index 2951b7133..cfa09b69a 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/parameter-select.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/parameter-select.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Parameter Select
Parameter select reports provide you with an easy way to add
interactivity into your dashboards.
diff --git a/docs/modules/ROOT/pages/user-guide/reports/pie-chart.adoc b/docs/modules/ROOT/pages/user-guide/reports/pie-chart.adoc
index 9300388a7..f4ad133b5 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/pie-chart.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/pie-chart.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Pie Chart
A pie chart will draw categories and values in a circular disc layout.
diff --git a/docs/modules/ROOT/pages/user-guide/reports/radar.adoc b/docs/modules/ROOT/pages/user-guide/reports/radar.adoc
index 4cd805e22..e6a4bcaf6 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/radar.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/radar.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Radar Chart
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/raw-json.adoc b/docs/modules/ROOT/pages/user-guide/reports/raw-json.adoc
index cf523529e..ec9725e83 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/raw-json.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/raw-json.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Raw JSON
The Raw JSON report renders the JSON response received from Neo4j
diff --git a/docs/modules/ROOT/pages/user-guide/reports/sankey.adoc b/docs/modules/ROOT/pages/user-guide/reports/sankey.adoc
index f725d2d3e..22d014803 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/sankey.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/sankey.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Sankey Chart
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/single-value.adoc b/docs/modules/ROOT/pages/user-guide/reports/single-value.adoc
index 593f92a11..f5a2f99c7 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/single-value.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/single-value.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Single Value
A single value report will render the first column of the first row
diff --git a/docs/modules/ROOT/pages/user-guide/reports/sunburst.adoc b/docs/modules/ROOT/pages/user-guide/reports/sunburst.adoc
index c3385bcdc..6f17c7541 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/sunburst.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/sunburst.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Sunburst
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/modules/ROOT/pages/user-guide/reports/table.adoc b/docs/modules/ROOT/pages/user-guide/reports/table.adoc
index 8bb11cb14..8b352f2c2 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/table.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/table.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Table
The most common report in a dashboard is often a simple table view.
diff --git a/docs/modules/ROOT/pages/user-guide/reports/treemap.adoc b/docs/modules/ROOT/pages/user-guide/reports/treemap.adoc
index 9a07ec12a..a8e620060 100644
--- a/docs/modules/ROOT/pages/user-guide/reports/treemap.adoc
+++ b/docs/modules/ROOT/pages/user-guide/reports/treemap.adoc
@@ -1,3 +1,5 @@
+include::../../banner.adoc[]
+
= Treemap
link:../../extensions/advanced-visualizations[label:Advanced Visualization[]]
diff --git a/docs/package-lock.json b/docs/package-lock.json
index 994e25648..6f4bd14e6 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -420,20 +420,20 @@
}
},
"node_modules/body-parser": {
- "version": "1.20.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
- "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
+ "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
- "content-type": "~1.0.4",
+ "content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
- "qs": "6.10.3",
- "raw-body": "2.5.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@@ -521,12 +521,18 @@
}
},
"node_modules/call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -662,9 +668,9 @@
}
},
"node_modules/content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
@@ -695,9 +701,9 @@
}
},
"node_modules/cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
@@ -753,6 +759,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
@@ -822,6 +844,25 @@
"once": "^1.4.0"
}
},
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -852,16 +893,16 @@
}
},
"node_modules/express": {
- "version": "4.18.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
- "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
+ "version": "4.19.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
+ "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
- "body-parser": "1.20.0",
+ "body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
- "cookie": "0.5.0",
+ "cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@@ -877,7 +918,7 @@
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
- "qs": "6.10.3",
+ "qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
@@ -1034,18 +1075,26 @@
}
},
"node_modules/function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/get-intrinsic": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
- "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dependencies": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.3"
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1137,6 +1186,17 @@
"node": ">=10.13.0"
}
},
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
@@ -1179,17 +1239,6 @@
"uglify-js": "^3.1.4"
}
},
- "node_modules/has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dependencies": {
- "function-bind": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4.0"
- }
- },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -1199,11 +1248,22 @@
}
},
"node_modules/has-property-descriptors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
- "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
- "get-intrinsic": "^1.1.1"
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1220,6 +1280,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/help-me": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-4.1.0.tgz",
@@ -1776,9 +1847,9 @@
}
},
"node_modules/object-inspect": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
- "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -2083,9 +2154,9 @@
}
},
"node_modules/qs": {
- "version": "6.10.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
- "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
@@ -2118,9 +2189,9 @@
}
},
"node_modules/raw-body": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
- "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@@ -2307,6 +2378,22 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -2330,13 +2417,17 @@
"integrity": "sha512-RPQhIndEIVUCjkfkQ6rs6sOR6pkxJWCNdxtfG5pP0RVgUYbK5911kLTF0TNcCC0G3YCGd492rMollFT2aTd9iQ=="
},
"node_modules/side-channel": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
- "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dependencies": {
- "call-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -3178,20 +3269,20 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
},
"body-parser": {
- "version": "1.20.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
- "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
+ "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"requires": {
"bytes": "3.1.2",
- "content-type": "~1.0.4",
+ "content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
- "qs": "6.10.3",
- "raw-body": "2.5.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
}
@@ -3246,12 +3337,15 @@
}
},
"call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"requires": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
}
},
"chokidar": {
@@ -3362,9 +3456,9 @@
}
},
"content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
},
"convert-source-map": {
"version": "1.8.0",
@@ -3391,9 +3485,9 @@
}
},
"cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
},
"cookie-signature": {
"version": "1.0.6",
@@ -3431,6 +3525,16 @@
"mimic-response": "^3.1.0"
}
},
+ "define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ }
+ },
"define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
@@ -3484,6 +3588,19 @@
"once": "^1.4.0"
}
},
+ "es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "requires": {
+ "get-intrinsic": "^1.2.4"
+ }
+ },
+ "es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
+ },
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -3505,16 +3622,16 @@
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
},
"express": {
- "version": "4.18.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
- "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
+ "version": "4.19.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
+ "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
- "body-parser": "1.20.0",
+ "body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
- "cookie": "0.5.0",
+ "cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@@ -3530,7 +3647,7 @@
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
- "qs": "6.10.3",
+ "qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
@@ -3661,18 +3778,20 @@
"optional": true
},
"function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"get-intrinsic": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
- "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"requires": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.3"
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
}
},
"glob": {
@@ -3747,6 +3866,14 @@
}
}
},
+ "gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "requires": {
+ "get-intrinsic": "^1.1.3"
+ }
+ },
"graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
@@ -3778,32 +3905,37 @@
"wordwrap": "^1.0.0"
}
},
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
"has-property-descriptors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
- "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"requires": {
- "get-intrinsic": "^1.1.1"
+ "es-define-property": "^1.0.0"
}
},
+ "has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q=="
+ },
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
},
+ "hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "requires": {
+ "function-bind": "^1.1.2"
+ }
+ },
"help-me": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-4.1.0.tgz",
@@ -4222,9 +4354,9 @@
}
},
"object-inspect": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
- "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ=="
},
"object-keys": {
"version": "1.1.1",
@@ -4478,9 +4610,9 @@
}
},
"qs": {
- "version": "6.10.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
- "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"requires": {
"side-channel": "^1.0.4"
}
@@ -4504,9 +4636,9 @@
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
- "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@@ -4642,6 +4774,19 @@
"send": "0.18.0"
}
},
+ "set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "requires": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ }
+ },
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -4662,13 +4807,14 @@
"integrity": "sha512-RPQhIndEIVUCjkfkQ6rs6sOR6pkxJWCNdxtfG5pP0RVgUYbK5911kLTF0TNcCC0G3YCGd492rMollFT2aTd9iQ=="
},
"side-channel": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
- "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"requires": {
- "call-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
}
},
"simple-concat": {
diff --git a/docs/preview.yml b/docs/preview.yml
index ed1a8f02e..038eb8bfb 100644
--- a/docs/preview.yml
+++ b/docs/preview.yml
@@ -12,7 +12,7 @@ content:
- '!**/README.adoc'
ui:
bundle:
- url: https://s3-eu-west-1.amazonaws.com/static-content.neo4j.com/build/ui-bundle.zip
+ url: https://static-content.neo4j.com/build/ui-bundle-latest.zip
snapshot: true
urls:
html_extension_style: indexify
diff --git a/docs/yarn.lock b/docs/yarn.lock
index 8c2a976bd..5f4adab6a 100644
--- a/docs/yarn.lock
+++ b/docs/yarn.lock
@@ -285,13 +285,13 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
-body-parser@1.20.1:
- version "1.20.1"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
- integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
+body-parser@1.20.2:
+ version "1.20.2"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
+ integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
dependencies:
bytes "3.1.2"
- content-type "~1.0.4"
+ content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
@@ -299,7 +299,7 @@ body-parser@1.20.1:
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
- raw-body "2.5.1"
+ raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@@ -429,10 +429,10 @@ content-disposition@0.5.4:
dependencies:
safe-buffer "5.2.1"
-content-type@~1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
- integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+content-type@~1.0.4, content-type@~1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
+ integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
convert-source-map@^1.5.0:
version "1.9.0"
@@ -452,10 +452,10 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
-cookie@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
- integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
+cookie@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
+ integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
core-util-is@~1.0.0:
version "1.0.3"
@@ -574,16 +574,16 @@ events@^3.3.0:
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
express@^4.17.1:
- version "4.18.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
- integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
+ version "4.19.2"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
+ integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
- body-parser "1.20.1"
+ body-parser "1.20.2"
content-disposition "0.5.4"
content-type "~1.0.4"
- cookie "0.5.0"
+ cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
@@ -1420,10 +1420,10 @@ range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
-raw-body@2.5.1:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
- integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
+raw-body@2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
+ integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
diff --git a/evolving.png b/evolving.png
new file mode 100644
index 000000000..ebc85364b
Binary files /dev/null and b/evolving.png differ
diff --git a/gallery/dashboards/fraud.json b/gallery/dashboards/fraud.json
index b75717ba0..b96145042 100644
--- a/gallery/dashboards/fraud.json
+++ b/gallery/dashboards/fraud.json
@@ -1,282 +1,324 @@
{
- "title": "Financial Crimes Enforcement Dashboard 🕵️",
- "version": "2.1",
- "settings": {
- "pagenumber": 0,
- "editable": true,
- "parameters": {
- "neodash_entity_name": null,
- "neodash_country_name": null
- },
- "fullscreenEnabled": true
+ "uuid": "b3236f88-ff8b-492d-8a84-d620a3dd629d",
+ "title": "Financial Crimes Enforcement Dashboard 🕵️",
+ "version": "2.4",
+ "settings": {
+ "pagenumber": 0,
+ "editable": true,
+ "parameters": {
+ "neodash_entity_name": null,
+ "neodash_country_name": null
},
- "pages": [
- {
- "title": "Countries",
- "reports": [
- {
- "x": 0,
- "y": 0,
- "title": "About this dashboard",
- "query": "This is an example dashboard on financial crime data. It uses the `fincen` dataset from \n[https://demo.neo4jlabs.com/](https://demo.neo4jlabs.com/).\n\nThis dashboard's purpose is to provide examples on how to use and customize all the different NeoDash report types.\n\nIt consists of three pages:\n- **Countries**: high-level data on specific countries.\n- **Investigate Entity**: a way to drill down into a specific entity.\n- **Statistics**: high-level statistics about the data.\n\nTry out the Documentation 📄 button on the left for basic examples of the different visualization reports.",
- "width": "4",
- "type": "text",
- "height": 2,
- "selection": {},
- "settings": {}
+ "fullscreenEnabled": true
+ },
+ "pages": [
+ {
+ "title": "Countries",
+ "reports": [
+ {
+ "x": 0,
+ "y": 0,
+ "title": "About this dashboard",
+ "query": "This is an example dashboard on financial crime data. It uses the `fincen` dataset from \n[https://demo.neo4jlabs.com/](https://demo.neo4jlabs.com/).\n\nThis dashboard's purpose is to provide examples on how to use and customize all the different NeoDash report types.\n\nIt consists of three pages:\n- **Countries**: high-level data on specific countries.\n- **Investigate Entity**: a way to drill down into a specific entity.\n- **Statistics**: high-level statistics about the data.\n\nTry out the Documentation 📄 button on the left for basic examples of the different visualization reports.",
+ "width": 8,
+ "type": "text",
+ "height": 4,
+ "selection": {},
+ "settings": {},
+ "id": "bd17ccad-c12e-4e5a-8e45-48504b071698"
+ },
+ {
+ "x": 8,
+ "y": 0,
+ "title": "How much does each entity benefit in total? (Hint: try clicking the table headers to sort/filter data)",
+ "query": "MATCH Path=(e:Entity)-[:COUNTRY]->(c:Country), (f:Filing)-[:BENEFITS]->(e)\nRETURN Path, e.name as Entity, c.name as Country, suM(f.amount) as `Total Benefit ($)`\nLIMIT 1000",
+ "width": 16,
+ "type": "table",
+ "height": 4,
+ "selection": {},
+ "settings": {},
+ "id": "6db3061c-12c5-4a92-a1a1-bf7e40c068a4"
+ },
+ {
+ "x": 0,
+ "y": 4,
+ "title": "Where in Europe does the Netherlands send money to?",
+ "query": "MATCH (c1:Country)--(:Entity)<-[:ORIGINATOR]-(f:Filing)-[:BENEFITS]->(:Entity)--(c2:Country)\nWHERE c1.name = \"Netherlands\"\nAND point.distance(c2.location, point({latitude: 53, longitude: 9})) < 3000000\nWITH c1, c2, sum(f.amount) as amount ORDER BY amount DESC\nRETURN c1, c2, apoc.create.vRelationship(c1, \"TRANSFER\", {amount: amount}, c2) ",
+ "width": 12,
+ "type": "map",
+ "height": 4,
+ "selection": {
+ "Country": "(no label)",
+ "TRANSFER": "(label)"
},
- {
- "x": 4,
- "y": 0,
- "title": "How much does each entity benefit in total? (Hint: try clicking the table headers to sort/filter data)",
- "query": "MATCH Path=(e:Entity)-[:COUNTRY]->(c:Country), (f:Filing)-[:BENEFITS]->(e)\nRETURN Path, e.name as Entity, c.name as Country, suM(f.amount) as `Total Benefit ($)`\nLIMIT 1000",
- "width": "8",
- "type": "table",
- "height": 2,
- "selection": {},
- "settings": {}
+ "settings": {
+ "defaultRelColor": "rgba(120,120,120,0.5)",
+ "defaultRelWidth": 5,
+ "defaultNodeSize": "medium",
+ "nodeColorScheme": "category10"
},
- {
- "x": 0,
- "y": 2,
- "title": "Where in Europe does the Netherlands send money to?",
- "query": "MATCH (c1:Country)--(:Entity)<-[:ORIGINATOR]-(f:Filing)-[:BENEFITS]->(:Entity)--(c2:Country)\nWHERE c1.name = \"Netherlands\"\nAND distance(c2.location, point({latitude: 53, longitude: 9})) < 3000000\nWITH c1, c2, sum(f.amount) as amount ORDER BY amount DESC\nRETURN c1, c2, apoc.create.vRelationship(c1, \"TRANSFER\", {amount: amount}, c2) ",
- "width": "6",
- "type": "map",
- "height": 2,
- "selection": {
- "Country": "(no label)"
- },
- "settings": {
- "defaultRelColor": "rgba(120,120,120,0.5)",
- "defaultRelWidth": 5,
- "defaultNodeSize": "medium",
- "nodeColorScheme": "category10"
- }
+ "id": "5484e81c-52b2-416d-8b7f-fa112887fbec",
+ "schema": [
+ ["Country", "code", "name", "location", "tld"],
+ ["TRANSFER", "amount"]
+ ]
+ },
+ {
+ "x": 12,
+ "y": 4,
+ "title": "Which entities are involved?",
+ "query": "MATCH (c1:Country)--(:Entity)<-[:ORIGINATOR]-(f:Filing)-[:BENEFITS]->(:Entity)--(c2:Country)\nWHERE c1.name = \"Netherlands\"\nAND point.distance(c2.location, point({latitude: 53, longitude: 9})) < 3000000\nWITH c1, c2, sum(f.amount) as amount\nWITH c1, c2, apoc.create.vRelationship(c1, \"TRANSFER\", {amount: amount}, c2) as t\n\nMATCH path=(c2:Country)-[r]-(e:Entity)\nRETURN c1, t, c2, collect(path)[0..10]",
+ "width": 12,
+ "type": "graph",
+ "height": 4,
+ "selection": {
+ "Country": "name",
+ "TRANSFER": "(label)",
+ "Entity": "name"
},
- {
- "x": 6,
- "y": 2,
- "title": "Which entities are involved?",
- "query": "MATCH (c1:Country)--(:Entity)<-[:ORIGINATOR]-(f:Filing)-[:BENEFITS]->(:Entity)--(c2:Country)\nWHERE c1.name = \"Netherlands\"\nAND distance(c2.location, point({latitude: 53, longitude: 9})) < 3000000\nWITH c1, c2, sum(f.amount) as amount\nWITH c1, c2, apoc.create.vRelationship(c1, \"TRANSFER\", {amount: amount}, c2) as t\n\nMATCH path=(c2:Country)-[r]-(e:Entity)\nRETURN c1, t, c2, collect(path)[0..10]",
- "width": "6",
- "type": "graph",
- "height": 2,
- "selection": {
- "Country": "name",
- "Entity": "name"
- },
- "settings": {
- "nodePositions": {}
- }
- }
- ]
- },
- {
- "title": "Entities",
- "reports": [
- {
- "x": 0,
- "y": 0,
- "title": "Entity Investigator 🔎",
- "query": "You can use this page to explore information about a single entity in the dataset. All reports are automatically updated based on the selected entity.\n\n**Hint**: Try typing **ING Bank NV** \nin the \"Entity name\" box to the right of this text.\n\n\n",
- "width": 3,
- "type": "text",
- "height": 2,
- "selection": {},
- "settings": {}
+ "settings": {
+ "nodePositions": {}
},
- {
- "x": 3,
- "y": 0,
- "title": "Select an entity to view reports",
- "query": "MATCH (n:`Entity`) \nWHERE toLower(toString(n.`name`)) CONTAINS toLower($input) \nRETURN DISTINCT n.`name` as value LIMIT 5",
- "width": 3,
- "type": "select",
- "height": 2,
- "selection": {},
- "settings": {
- "type": "Node Property",
- "entityType": "Entity",
- "propertyType": "name",
- "parameterName": "neodash_entity_name"
- }
+ "id": "a4f84cff-996b-4bfd-b5de-2d8f0c9aa8b1",
+ "schema": [
+ ["Country", "code", "name", "location", "tld"],
+ ["TRANSFER", "amount"],
+ ["Entity", "name", "location", "id", "country"]
+ ]
+ }
+ ]
+ },
+ {
+ "title": "Entities",
+ "reports": [
+ {
+ "x": 0,
+ "y": 0,
+ "title": "Entity Investigator 🔎",
+ "query": "You can use this page to explore information about a single entity in the dataset. All reports are automatically updated based on the selected entity.\n\n**Hint**: Try typing **ING Bank NV** \nin the \"Entity name\" box to the right of this text.\n\n\n",
+ "width": 6,
+ "type": "text",
+ "height": 4,
+ "selection": {},
+ "settings": {},
+ "id": "e33482d3-a7a2-4090-8868-0ed3931bc99e"
+ },
+ {
+ "x": 6,
+ "y": 0,
+ "title": "Select an entity to view reports",
+ "query": "MATCH (n:`Entity`) \nWHERE toLower(toString(n.`name`)) CONTAINS toLower($input) \nRETURN DISTINCT n.`name` as value, n.`name` as display ORDER BY size(toString(value)) ASC LIMIT 5",
+ "width": 5,
+ "type": "select",
+ "height": 4,
+ "selection": {},
+ "settings": {
+ "type": "Node Property",
+ "entityType": "Entity",
+ "propertyType": "name",
+ "parameterName": "neodash_entity_name"
+ },
+ "id": "5a5a46cb-d586-4831-88bc-b193edfa9e9c"
+ },
+ {
+ "x": 11,
+ "y": 0,
+ "title": "Details ",
+ "query": " MATCH (e:Entity)\nWHERE e.name = $neodash_entity_name\nWITH e LIMIT 1\nMATCH (c:Country)--(e)--(f:Filing)\nWITH e, c, sum(f.amount) AS totalAmount, min(f.begin) AS startOperation\nWITH e, c, totalAmount, startOperation\nRETURN e.name as `Entity full name`,\n c.name as `Country of origin`,\n \"$\" + toInteger(totalAmount/1000000) + \" million\" as `Total filings`,\n toString(date(startOperation)) as `Start of operations`\n",
+ "width": 7,
+ "type": "table",
+ "height": 4,
+ "selection": {},
+ "settings": {
+ "compact": false
+ },
+ "id": "6e2e57b7-09c8-46cf-b33e-19fd3645693b",
+ "schema": []
+ },
+ {
+ "x": 18,
+ "y": 0,
+ "title": "Entity interactions",
+ "query": "MATCH path=(e:Entity)<--()-->(e2:Entity)\nWHERE e.name = $neodash_entity_name\nWITH DISTINCT e, e2\nRETURN e, e2, apoc.create.vRelationship(e, \"INTERACTS\", {}, e2) \n\n\n",
+ "width": 6,
+ "type": "map",
+ "height": 4,
+ "selection": {
+ "Entity": "(no label)",
+ "INTERACTS": "(label)"
},
- {
- "x": 6,
- "y": 0,
- "title": "Details ",
- "query": "MATCH (e:Entity)\nWHERE e.name = $neodash_entity_name\nWITH e LIMIT 1\nMATCH (c:Country)--(e)--(f:Filing)\nWITH [\"Entity full name: \" + e.name, \n \"Country of origin: \"+c.name, \n \"Total filings: $\"+ toInteger(sum(f.amount)/1000000) + \" million\",\n \"Start of operations: \"+ toString(date((min(f.begin))))\n] as data\nUNWIND data as Information\nRETURN Information\n",
- "width": "3",
- "type": "table",
- "height": 2,
- "selection": {},
- "settings": {}
+ "settings": {
+ "hideSelections": true
},
- {
- "x": 9,
- "y": 0,
- "title": "Entity interactions",
- "query": "MATCH path=(e:Entity)<--()-->(e2:Entity)\nWHERE e.name = $neodash_entity_name\nWITH DISTINCT e, e2\nRETURN e, e2, apoc.create.vRelationship(e, \"INTERACTS\", {}, e2) \n\n\n",
- "width": 3,
- "type": "map",
- "height": 2,
- "selection": {
- "Entity": "(no label)"
- },
- "settings": {
- "hideSelections": true
- }
+ "id": "6070f205-23ce-42bb-abc1-96ec3839e531",
+ "schema": [["Entity", "name", "location", "id", "country"], ["INTERACTS"]]
+ },
+ {
+ "x": 0,
+ "y": 4,
+ "title": "Who receives most money from this entity?",
+ "query": "MATCH path=(e:Entity)<--(f:Filing)-->(e2:Entity)\nWHERE e.name = $neodash_entity_name\nWITH DISTINCT e, f, e2\nRETURN e2.name as `Other`, sum(f.amount) as Amount\nORDER BY Amount ASC",
+ "width": 12,
+ "type": "bar",
+ "height": 4,
+ "selection": {
+ "index": "Other",
+ "value": "Amount",
+ "key": "(none)"
},
- {
- "x": 0,
- "y": 2,
- "title": "Who receives most money from this entity?",
- "query": "MATCH path=(e:Entity)<--(f:Filing)-->(e2:Entity)\nWHERE e.name = $neodash_entity_name\nWITH DISTINCT e, f, e2\nRETURN e2.name as `Other`, sum(f.amount) as Amount\nORDER BY Amount ASC",
- "width": "6",
- "type": "bar",
- "height": 2,
- "selection": {
- "index": "Other",
- "value": "Amount",
- "key": "(none)"
- },
- "settings": {
- "valueScale": "linear",
- "marginLeft": 90,
- "marginBottom": 100,
- "marginRight": 50,
- "colors": "paired",
- "groupMode": "grouped"
- }
+ "settings": {
+ "valueScale": "linear",
+ "marginLeft": 90,
+ "marginBottom": 100,
+ "marginRight": 50,
+ "colors": "paired",
+ "groupMode": "grouped"
},
- {
- "x": 6,
- "y": 2,
- "title": "Details on a filing by the entity",
- "query": "MATCH path=(e:Entity)<--(f:Filing)\nWHERE e.name = $neodash_entity_name\nRETURN f LIMIT 1\n",
- "width": 3,
- "type": "json",
- "height": 2,
- "selection": {},
- "settings": {}
+ "id": "7023ae0c-68af-4fa6-8c59-3ba91d7980aa"
+ },
+ {
+ "x": 12,
+ "y": 4,
+ "title": "Details on a filing by the entity",
+ "query": "MATCH path=(e:Entity)<--(f:Filing)\nWHERE e.name = $neodash_entity_name\nRETURN f LIMIT 1\n",
+ "width": 6,
+ "type": "json",
+ "height": 4,
+ "selection": {},
+ "settings": {},
+ "id": "ceb4d37e-8d9c-45c9-9062-485d40bed6cd"
+ },
+ {
+ "x": 18,
+ "y": 4,
+ "title": "Number of Filings",
+ "query": "MATCH (e:Entity)--(:Filing)\nWHERE e.name = $neodash_entity_name\nRETURN COUNT(*)\n\n",
+ "width": 6,
+ "type": "value",
+ "height": 4,
+ "selection": {},
+ "settings": {
+ "fontSize": 80
+ },
+ "id": "0efecd19-10ea-4475-b649-19b3b1b1e511"
+ }
+ ]
+ },
+ {
+ "title": "Statistics",
+ "reports": [
+ {
+ "x": 0,
+ "y": 0,
+ "title": "Total number of nodes",
+ "query": "MATCH (n)\nRETURN COUNT(n)",
+ "width": 6,
+ "type": "value",
+ "height": 4,
+ "selection": {},
+ "settings": {
+ "textAlign": "center",
+ "fontSize": 80,
+ "marginTop": 50
},
- {
- "x": 9,
- "y": 2,
- "title": "Number of Filings",
- "query": "MATCH (e:Entity)--(:Filing)\nWHERE e.name = $neodash_entity_name\nRETURN COUNT(*)\n\n",
- "width": 3,
- "type": "value",
- "height": 2,
- "selection": {},
- "settings": {
- "fontSize": 80
- }
- }
- ]
- },
- {
- "title": "Statistics",
- "reports": [
- {
- "x": 0,
- "y": 0,
- "title": "Total number of nodes",
- "query": "MATCH (n)\nRETURN COUNT(n)",
- "width": 3,
- "type": "value",
- "height": 2,
- "selection": {},
- "settings": {
- "textAlign": "center",
- "fontSize": 80,
- "marginTop": 50
- }
+ "id": "073fc2f0-c1a8-4206-ac48-53171ed98696"
+ },
+ {
+ "x": 6,
+ "y": 0,
+ "title": "Total number of relationships",
+ "query": "MATCH (n)-[e]->(m)\nRETURN COUNT(e)\n\n\n",
+ "width": 6,
+ "type": "value",
+ "height": 4,
+ "selection": {},
+ "settings": {
+ "fontSize": 80,
+ "marginTop": 50,
+ "textAlign": "center"
},
- {
- "x": 3,
- "y": 0,
- "title": "Total number of relationships",
- "query": "MATCH (n)-[e]->(m)\nRETURN COUNT(e)\n\n\n",
- "width": 3,
- "type": "value",
- "height": 2,
- "selection": {},
- "settings": {
- "fontSize": 80,
- "marginTop": 50,
- "textAlign": "center"
- }
+ "id": "294a7cc4-bd1e-4d6f-b440-16e9ad9a95f8"
+ },
+ {
+ "x": 12,
+ "y": 0,
+ "title": "Number of nodes by label",
+ "query": "MATCH (n)\nRETURN labels(n), count(*) as count\nORDER BY count ASC\n\n\n",
+ "width": 6,
+ "type": "pie",
+ "height": 4,
+ "selection": {
+ "index": "labels(n)",
+ "value": "count",
+ "key": "(none)"
},
- {
- "x": 6,
- "y": 0,
- "title": "Number of nodes by label",
- "query": "MATCH (n)\nRETURN labels(n), count(*) as count\nORDER BY count ASC\n\n\n",
- "width": 3,
- "type": "pie",
- "height": 2,
- "selection": {
- "index": "labels(n)",
- "value": "count",
- "key": "(none)"
- },
- "settings": {
- "colors": "pastel1",
- "marginBottom": 60
- }
+ "settings": {
+ "colors": "pastel1",
+ "marginBottom": 60
},
- {
- "x": 9,
- "y": 0,
- "title": "Number of relationship types",
- "query": "MATCH (n)-[e]->(m)\nRETURN type(e),count(*) as count\nORDER BY count ASC\n\n\n\n\n\n\n",
- "width": 3,
- "type": "pie",
- "height": 2,
- "selection": {
- "index": "type(e)",
- "value": "count",
- "key": "(none)"
- },
- "settings": {
- "colors": "pastel1",
- "marginBottom": 60,
- "marginLeft": 120,
- "marginRight": 120
- }
+ "id": "bb995076-94b4-4e58-822b-19d4f8c62ac8"
+ },
+ {
+ "x": 18,
+ "y": 0,
+ "title": "Number of relationship types",
+ "query": "MATCH (n)-[e]->(m)\nRETURN type(e),count(*) as count\nORDER BY count ASC\n\n\n\n\n\n\n",
+ "width": 6,
+ "type": "pie",
+ "height": 4,
+ "selection": {
+ "index": "type(e)",
+ "value": "count",
+ "key": "(none)"
},
- {
- "x": 0,
- "y": 2,
- "title": "Number of filing per year",
- "query": "MATCH (f:Filing)\nWHERE f.begin IS NOT NULL\nWITH f, date(f.begin).year as Year\nRETURN Year, COUNT(f) as Total\nORDER BY Year ASC\n",
- "width": 6,
- "type": "line",
- "height": 2,
- "selection": {
- "x": "Year",
- "value": [
- "Total"
- ]
- },
- "settings": {
- "marginLeft": 60
- }
+ "settings": {
+ "colors": "pastel1",
+ "marginBottom": 60,
+ "marginLeft": 120,
+ "marginRight": 120
},
- {
- "x": 6,
- "y": 2,
- "title": "Example: using iFrames to embed custom visualizations (3D graph)",
- "query": "https://vasturiano.github.io/react-force-graph/example/basic/",
- "width": 6,
- "type": "iframe",
- "height": 2,
- "selection": {},
- "settings": {}
- }
- ]
- }
- ]
- }
\ No newline at end of file
+ "id": "6b604fad-6104-49e7-9aea-0a878a887e49"
+ },
+ {
+ "x": 0,
+ "y": 4,
+ "title": "Number of filing per year",
+ "query": "MATCH (f:Filing)\nWHERE f.begin IS NOT NULL\nWITH f, date(f.begin).year as Year\nRETURN Year, COUNT(f) as Total\nORDER BY Year ASC\n",
+ "width": 12,
+ "type": "line",
+ "height": 4,
+ "selection": {
+ "x": "Year",
+ "value": ["Total"]
+ },
+ "settings": {
+ "marginLeft": 60
+ },
+ "id": "93ce2eab-4f7e-4e6e-a71e-257ebf5f68a9"
+ },
+ {
+ "x": 12,
+ "y": 4,
+ "title": "Example: using iFrames to embed custom visualizations (3D graph)",
+ "query": "https://vasturiano.github.io/react-force-graph/example/basic/",
+ "width": 12,
+ "type": "iframe",
+ "height": 4,
+ "selection": {},
+ "settings": {},
+ "id": "94463630-ed46-4cbe-b140-316151e23ed1"
+ }
+ ]
+ }
+ ],
+ "extensions": {
+ "advanced-charts": {
+ "active": true
+ },
+ "styling": {
+ "active": true
+ },
+ "active": true,
+ "activeReducers": []
+ }
+}
diff --git a/gallery/yarn.lock b/gallery/yarn.lock
index 8767f60fd..aa9a0cde5 100644
--- a/gallery/yarn.lock
+++ b/gallery/yarn.lock
@@ -2994,13 +2994,13 @@ bluebird@^3.5.5:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
-body-parser@1.20.1:
- version "1.20.1"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
- integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
+body-parser@1.20.2:
+ version "1.20.2"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
+ integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
dependencies:
bytes "3.1.2"
- content-type "~1.0.4"
+ content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
@@ -3008,7 +3008,7 @@ body-parser@1.20.1:
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
- raw-body "2.5.1"
+ raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@@ -3042,12 +3042,19 @@ brace-expansion@^2.0.1:
dependencies:
balanced-match "^1.0.0"
-braces@^3.0.2, braces@~3.0.2:
+braces@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+ integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+ dependencies:
+ fill-range "^7.1.1"
+
+braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
- fill-range "^7.0.1"
+ fill-range "^7.1.1"
browser-process-hrtime@^1.0.0:
version "1.0.0"
@@ -3395,7 +3402,7 @@ content-disposition@0.5.4:
dependencies:
safe-buffer "5.2.1"
-content-type@~1.0.4:
+content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
@@ -3410,10 +3417,10 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
-cookie@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
- integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
+cookie@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
+ integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
copy-to-clipboard@^3.3.1:
version "3.3.3"
@@ -3968,9 +3975,9 @@ ee-first@1.1.1:
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
ejs@^3.1.6:
- version "3.1.9"
- resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
- integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
+ version "3.1.10"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
+ integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==
dependencies:
jake "^10.8.5"
@@ -4467,16 +4474,16 @@ expect@^27.5.1:
jest-message-util "^27.5.1"
express@^4.17.3:
- version "4.18.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
- integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
+ version "4.19.2"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
+ integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
- body-parser "1.20.1"
+ body-parser "1.20.2"
content-disposition "0.5.4"
content-type "~1.0.4"
- cookie "0.5.0"
+ cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
@@ -4530,9 +4537,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fast-loops@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75"
- integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.4.tgz#61bc77d518c0af5073a638c6d9d5c7683f069ce2"
+ integrity sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg==
fast-shallow-equal@^1.0.0:
version "1.0.0"
@@ -4606,10 +4613,17 @@ filesize@^8.0.6:
resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8"
integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
@@ -4677,9 +4691,9 @@ flatted@^3.1.0:
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
follow-redirects@^1.0.0:
- version "1.15.2"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
- integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
+ version "1.15.6"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
+ integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
for-each@^0.3.3:
version "0.3.3"
@@ -6476,11 +6490,11 @@ methods@~1.1.2:
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
- integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+ integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
- braces "^3.0.2"
+ braces "^3.0.3"
picomatch "^2.3.1"
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
@@ -7735,10 +7749,10 @@ range-parser@^1.2.1, range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
-raw-body@2.5.1:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
- integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
+raw-body@2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
+ integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
@@ -9338,9 +9352,9 @@ webidl-conversions@^6.1.0:
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
webpack-dev-middleware@^5.3.1:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f"
- integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==
+ version "5.3.4"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517"
+ integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==
dependencies:
colorette "^2.0.10"
memfs "^3.4.3"
diff --git a/k8s-deploy/neodash/.helmignore b/k8s-deploy/neodash/.helmignore
new file mode 100644
index 000000000..0e8a0eb36
--- /dev/null
+++ b/k8s-deploy/neodash/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/k8s-deploy/neodash/Chart.yaml b/k8s-deploy/neodash/Chart.yaml
new file mode 100644
index 000000000..41e4e6c4e
--- /dev/null
+++ b/k8s-deploy/neodash/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: neodash
+description: A NeoDash Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 1.0.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "2.4.9"
\ No newline at end of file
diff --git a/k8s-deploy/neodash/README.md b/k8s-deploy/neodash/README.md
new file mode 100644
index 000000000..0185710c9
--- /dev/null
+++ b/k8s-deploy/neodash/README.md
@@ -0,0 +1,78 @@
+# NeoDash
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational?style=flat-square)
+
+A NeoDash Helm chart for Kubernetes
+
+## Resources
+
+Following are the Kubernetes resources utilized for the NeoDash.
+
+- Deployment
+- Service
+- Ingress
+- Service Account
+- Horizontal Pod Autoscalar (HPA)
+
+## Values Configuration
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| autoscaling.enabled | bool | `false` | Enable/disable Autoscaling |
+| enable_reader_mode | bool | `true` | Enable/disable Reader mode |
+| envFromSecrets | list | `[]` | Environment variables from secrets |
+| fullnameOverride | string | `"neodash-test"` | Name override applies to all resources |
+| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
+| image.repository | string | `"neo4jlabs/neodash"` | Image repository and Image name |
+| image.tag | string | `"latest"` | Image version |
+| imagePullSecrets | list | `[]` | Image pull secrets if any |
+| podAnnotations | object | `{}` | Pod annotations |
+| podLabels | object | `{}` | Additional labels |
+| podSecurityContext | object | `{}` | Security Context if any |
+| ingress.annotations | object | `{}` | Ingress Annotations for load balancers |
+| ingress.className | string | `"alb"` | Ingress Class |
+| ingress.enabled | bool | `false` | Enable/disable Ingress |
+| ingress.hosts | list | `[]` | Host Details |
+| ingress.tls | list | `[]` | TLS details |
+| livenessProbe.httpGet.path | string | `"/*"` | LivenessProbe path |
+| livenessProbe.httpGet.port | int | `5005` | LivenessProbe port |
+| readinessProbe.httpGet.path | string | `"/*"` | Readiness path |
+| readinessProbe.httpGet.port | int | `5005` | Readiness port |
+| replicaCount | int | `1` | Replica count |
+| resources.limits.cpu | string | `"500m"` | CPU limit |
+| resources.limits.memory | string | `"128Mi"` | Memory limit |
+| resources.requests.cpu | string | `"250m"` | CPU request |
+| resources.requests.memory | string | `"64Mi"` | Memory request |
+| service.annotations | object | `{}` | Service annotations |
+| service.port | int | `5005` | Service port |
+| service.targetPort | int | `5005` | Service target port |
+| service.type | string | `"LoadBalancer"` | Type of service, other options are `ClusterIP` or `NodePort` |
+| serviceAccount.automount | bool | `true` | Enable/disable service account auto mount to pod |
+| serviceAccount.create | bool | `true` | Enable/disable service account |
+| volumeMounts | list | `[]` | Volume mounts on pod |
+| volumes | list | `[]` | Volumes for pod |
+| env | list |
- name: "ssoEnabled"
value: "false"
- name: "standalone"
value: "true"
- name: "standaloneProtocol"
value: "neo4j+s"
- name: "standaloneHost"
value: "localhost"
- name: "standalonePort"
value: "7687"
- name: "standaloneDatabase"
value: "neo4j"
- name: "standaloneDashboardName"
value: "test"
- name: "standaloneDashboardDatabase"
value: "neo4j"
- name: "standaloneAllowLoad"
value: "false"
- name: "standaloneLoadFromOtherDatabases"
value: "false"
- name: "standaloneMultiDatabase"
value: "false"
| Env variables for reader mode |
+
+## Usage
+
+- To install this helm chart run the following command,
+
+ ```bash
+ helm install ./neodash -n
+ ```
+
+- To upgrade the release run the following command,
+
+ ```bash
+ helm upgrade ./neodash -n
+ ```
+
+- To uninstall the release run the following command,
+
+ ```bash
+ helm uninstall -n
+ ```
+
+> **Note:** To use custom values files, pass `-f .yaml` for the above command.
+> **Note:** To use custom values, pass `--set param=value` for the above command.
+For example, to install neodash and set the service type to NodePort, run: `helm install ./neodash -n --set service.type=NodePort`
diff --git a/k8s-deploy/neodash/templates/NOTES.txt b/k8s-deploy/neodash/templates/NOTES.txt
new file mode 100644
index 000000000..099242813
--- /dev/null
+++ b/k8s-deploy/neodash/templates/NOTES.txt
@@ -0,0 +1,26 @@
+The NeoDash application has been successfully deployed, here is the application URL:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ Run the following command to retrieve the IP address:
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "neodash.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of the LoadBalancer by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "neodash.fullname" . }}'
+
+ Once available, run the following command to retrieve the IP address:
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "neodash.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ Run the following command to retrieve the IP address:
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "neodash.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
\ No newline at end of file
diff --git a/k8s-deploy/neodash/templates/_helpers.tpl b/k8s-deploy/neodash/templates/_helpers.tpl
new file mode 100644
index 000000000..9bb54f5c5
--- /dev/null
+++ b/k8s-deploy/neodash/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neodash.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neodash.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neodash.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neodash.labels" -}}
+helm.sh/chart: {{ include "neodash.chart" . }}
+{{ include "neodash.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neodash.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neodash.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neodash.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neodash.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/k8s-deploy/neodash/templates/deployment.yaml b/k8s-deploy/neodash/templates/deployment.yaml
new file mode 100644
index 000000000..63778a9c1
--- /dev/null
+++ b/k8s-deploy/neodash/templates/deployment.yaml
@@ -0,0 +1,76 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "neodash.fullname" . }}
+ labels:
+ {{- include "neodash.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "neodash.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "neodash.labels" . | nindent 8 }}
+ {{- with .Values.podLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "neodash.serviceAccountName" . }}
+ automountServiceAccountToken: false
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ {{- with .Values.podSecurityContext }}
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: {{ .Values.service.targetPort }}
+ protocol: TCP
+ env:
+ {{- if ne 5005 (int .Values.service.targetPort) }}
+ - name: NGINX_PORT
+ value: {{ .Values.service.port | quote }}
+ {{- end }}
+ {{- if .Values.enable_reader_mode}}
+ {{- with .Values.env }}
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- if .Values.envFromSecrets }}
+ {{- range $key, $value := .Values.envFromSecrets }}
+ - name: {{ $key }}
+ valueFrom:
+ secretKeyRef:
+ name: {{ $value.secretName }}
+ key: {{ $value.key }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ livenessProbe:
+ {{- toYaml .Values.livenessProbe | nindent 12 }}
+ readinessProbe:
+ {{- toYaml .Values.readinessProbe | nindent 12 }}
+ resources:
+ {{- toYaml .Values.resources | nindent 12 }}
+ {{- with .Values.volumeMounts }}
+ volumeMounts:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.volumes }}
+ volumes:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
\ No newline at end of file
diff --git a/k8s-deploy/neodash/templates/hpa.yaml b/k8s-deploy/neodash/templates/hpa.yaml
new file mode 100644
index 000000000..ce2bae2db
--- /dev/null
+++ b/k8s-deploy/neodash/templates/hpa.yaml
@@ -0,0 +1,32 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "neodash.fullname" . }}
+ labels:
+ {{- include "neodash.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "neodash.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/k8s-deploy/neodash/templates/ingress.yaml b/k8s-deploy/neodash/templates/ingress.yaml
new file mode 100644
index 000000000..8dccdc279
--- /dev/null
+++ b/k8s-deploy/neodash/templates/ingress.yaml
@@ -0,0 +1,61 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "neodash.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
+ {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
+ {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
+ {{- end }}
+{{- end }}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "neodash.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
+ ingressClassName: {{ .Values.ingress.className }}
+ {{- end }}
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
+ pathType: {{ .pathType }}
+ {{- end }}
+ backend:
+ {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
+ service:
+ name: {{ $fullName }}
+ port:
+ number: {{ $svcPort }}
+ {{- else }}
+ serviceName: {{ $fullName }}
+ servicePort: {{ $svcPort }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/k8s-deploy/neodash/templates/service.yaml b/k8s-deploy/neodash/templates/service.yaml
new file mode 100644
index 000000000..b1080ab59
--- /dev/null
+++ b/k8s-deploy/neodash/templates/service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "neodash.fullname" . }}
+ labels:
+ {{- include "neodash.labels" . | nindent 4 }}
+ {{- with .Values.service.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: {{ .Values.service.targetPort }}
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "neodash.selectorLabels" . | nindent 4 }}
\ No newline at end of file
diff --git a/k8s-deploy/neodash/templates/serviceaccount.yaml b/k8s-deploy/neodash/templates/serviceaccount.yaml
new file mode 100644
index 000000000..a7dd2c3ac
--- /dev/null
+++ b/k8s-deploy/neodash/templates/serviceaccount.yaml
@@ -0,0 +1,13 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "neodash.serviceAccountName" . }}
+ labels:
+ {{- include "neodash.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
+{{- end }}
diff --git a/k8s-deploy/neodash/templates/tests/test-connection.yaml b/k8s-deploy/neodash/templates/tests/test-connection.yaml
new file mode 100644
index 000000000..b7def4d87
--- /dev/null
+++ b/k8s-deploy/neodash/templates/tests/test-connection.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "neodash.fullname" . }}-test-connection"
+ labels:
+ {{- include "neodash.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ automountServiceAccountToken: false
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "neodash.fullname" . }}:{{ .Values.service.port }}']
+ resources:
+ {{- toYaml .Values.resources | nindent 8 }}
+ restartPolicy: Never
diff --git a/k8s-deploy/neodash/values.yaml b/k8s-deploy/neodash/values.yaml
new file mode 100644
index 000000000..56b781213
--- /dev/null
+++ b/k8s-deploy/neodash/values.yaml
@@ -0,0 +1,108 @@
+# Name override or full name override
+nameOverride: ''
+fullnameOverride: neodash-test
+
+# Number of pods
+replicaCount: 1
+
+# Image Details
+image:
+ repository: neo4jlabs/neodash
+ pullPolicy: IfNotPresent
+ tag: 'latest'
+imagePullSecrets: [] # Image pull secret if any
+
+# Pod annotations, labels and security context
+podAnnotations: {}
+podLabels: {}
+podSecurityContext: {}
+
+# Mode configuration using environment variables
+# Set reader mode environment variables when enable_reader_mode is true
+enable_reader_mode: true
+env:
+ - name: "ssoEnabled"
+ value: "false"
+ - name: "standalone"
+ value: "true"
+ - name: "standaloneProtocol"
+ value: "neo4j+s"
+ - name: "standaloneHost"
+ value: "localhost"
+ - name: "standalonePort"
+ value: "7687"
+ - name: "standaloneDatabase"
+ value: neo4j
+ - name: "standaloneDashboardName"
+ value: "test"
+ - name: "standaloneDashboardDatabase"
+ value: neo4j
+ - name: "standaloneAllowLoad"
+ value: "false"
+ - name: "standaloneLoadFromOtherDatabases"
+ value: "false"
+ - name: "standaloneMultiDatabase"
+ value: "false"
+
+# Environment variable from secret
+envFromSecrets: []
+ # standaloneUsername:
+ # secretName: "neo4j-connection-secrets"
+ # key: "username"
+ # standalonePassword:
+ # secretName: "neo4j-connection-secrets"
+ # key: "password"
+
+# Service details
+service:
+ type: LoadBalancer # Can also be ClusterIP or NodePort
+ port: 5005 # For the service to listen in for Traffic
+ targetPort: 5005 # Target port is the container port
+ annotations: {} # Service annotations for the LoadBalance
+
+# Ingress
+ingress:
+ enabled: false # Enable Kubernetes Ingress
+ className: 'alb' # Class Name
+ annotations: {} # Cloud LoadBalancer annotations
+ hosts: []
+ # - host: neodash.example.com
+ # paths:
+ # - path: '/'
+ # pathType: Prefix
+ tls: []
+
+# Pod resources request, limits and health check
+resources:
+ requests:
+ memory: "64Mi"
+ cpu: "250m"
+ limits:
+ memory: "128Mi"
+ cpu: "500m"
+livenessProbe:
+ httpGet:
+ path: /*
+ port: 5005
+readinessProbe:
+ httpGet:
+ path: /*
+ port: 5005
+
+# Pod Autoscaler
+autoscaling:
+ enabled: false
+ # minReplicas: 1
+ # maxReplicas: 100
+ # targetCPUUtilizationPercentage: 80
+
+# Pod Volumes
+volumes: []
+volumeMounts: []
+
+# Service Account
+serviceAccount:
+ create: true
+ automount: true
+ # annotations: {}
+ # name: ''
\ No newline at end of file
diff --git a/k8s-deploy/sample-k8s-yamls/deployment.yaml b/k8s-deploy/sample-k8s-yamls/deployment.yaml
new file mode 100644
index 000000000..e9e490a27
--- /dev/null
+++ b/k8s-deploy/sample-k8s-yamls/deployment.yaml
@@ -0,0 +1,66 @@
+---
+# Source: neodash/templates/deployment.yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: neodash
+ labels:
+ application: neodash-deploy
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ application: neodash-deploy
+ template:
+ metadata:
+ labels:
+ application: neodash-deploy
+ spec:
+ serviceAccountName: neodash-test
+ automountServiceAccountToken: false
+ containers:
+ - name: neodash
+ image: "neo4jlabs/neodash:latest"
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: http
+ containerPort: 5005
+ protocol: TCP
+ env:
+ - name: ssoEnabled
+ value: "false"
+ - name: standalone
+ value: "true"
+ - name: standaloneProtocol
+ value: neo4j+s
+ - name: standaloneHost
+ value: localhost
+ - name: standalonePort
+ value: "7687"
+ - name: standaloneDatabase
+ value: neo4j
+ - name: standaloneDashboardName
+ value: test
+ - name: standaloneDashboardDatabase
+ value: neo4j
+ - name: standaloneAllowLoad
+ value: "false"
+ - name: standaloneLoadFromOtherDatabases
+ value: "false"
+ - name: standaloneMultiDatabase
+ value: "false"
+ livenessProbe:
+ httpGet:
+ path: /*
+ port: 5005
+ readinessProbe:
+ httpGet:
+ path: /*
+ port: 5005
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 250m
+ memory: 64Mi
\ No newline at end of file
diff --git a/k8s-deploy/sample-k8s-yamls/service.yaml b/k8s-deploy/sample-k8s-yamls/service.yaml
new file mode 100644
index 000000000..28633b71c
--- /dev/null
+++ b/k8s-deploy/sample-k8s-yamls/service.yaml
@@ -0,0 +1,17 @@
+---
+# Source: neodash/templates/service.yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: neodash
+ labels:
+ application: neodash-deploy
+spec:
+ type: LoadBalancer
+ ports:
+ - port: 5005
+ targetPort: 5005
+ protocol: TCP
+ name: http
+ selector:
+ application: neodash-deploy
\ No newline at end of file
diff --git a/package.json b/package.json
index 495aabedb..f52801638 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "neodash",
- "version": "2.4.2",
+ "version": "2.4.9",
"description": "NeoDash - Neo4j Dashboard Builder",
"neo4jDesktop": {
"apiVersion": "^1.2.0"
@@ -30,7 +30,8 @@
"lint": "eslint --ext .ts --ext .tsx .",
"lint-staged": "lint-staged --config .lintstagedrc.json",
"test": "yarn cypress open",
- "test-headless": "yarn cypress run"
+ "test-headless": "yarn cypress run",
+ "prepare": "husky install"
},
"keywords": [],
"author": "Neo4j Labs",
@@ -41,7 +42,7 @@
"@dnd-kit/sortable": "^7.0.2",
"@mui/material": "^5.12.3",
"@mui/styles": "^5.12.3",
- "@mui/x-data-grid": "5.17.26",
+ "@mui/x-data-grid": "7.4.0",
"@mui/x-date-pickers": "^5.0.17",
"@neo4j-cypher/react-codemirror": "^1.0.3",
"@neo4j-ndl/base": "1.10.3",
@@ -65,6 +66,7 @@
"d3-scale-chromatic": "^3.0.0",
"dayjs": "^1.11.7",
"dom-to-image": "^2.6.0",
+ "dompurify": "^3.1.0",
"leaflet": "^1.7.1",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
@@ -100,6 +102,7 @@
"tailwindcss": "^3.3.2",
"three": "^0.159.0",
"three-spritetext": "^1.8.1",
+ "urijs": "^1.19.11",
"use-neo4j": "^0.3.13",
"yaml": "^2.2.1"
},
diff --git a/public/accesscontrol2.jpg b/public/accesscontrol2.jpg
new file mode 100644
index 000000000..be948e1b2
Binary files /dev/null and b/public/accesscontrol2.jpg differ
diff --git a/public/style.css b/public/style.css
index 5896897bf..63cf9fca1 100644
--- a/public/style.css
+++ b/public/style.css
@@ -125,17 +125,13 @@
margin-top: -10px;
}
-.MuiDataGrid-virtualScroller {
- overflow-y: hidden !important;
-}
-
-.MuiDataGrid-panel{
+.MuiDataGrid-panel {
translate: 0px -152%;
}
.MuiCard-root {
- box-shadow: 0 0 #0000,0 0 #0000,var(--tw-shadow) !important;
- box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow) !important;
+ box-shadow: 0 0 #0000, 0 0 #0000, var(--tw-shadow) !important;
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;
}
.white-text {
@@ -188,7 +184,6 @@
text-align: center;
}
-
.card-view.expanded {
position: absolute;
top: 0;
@@ -218,24 +213,23 @@
.card-view .MuiTablePagination-root {
margin-top: 0px;
-
}
@keyframes pulse {
- 0% {
- transform: scale(0.95);
- box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
- }
+ 0% {
+ transform: scale(0.95);
+ box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
+ }
- 70% {
- transform: scale(1);
- box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
- }
+ 70% {
+ transform: scale(1);
+ box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
+ }
- 100% {
- transform: scale(0.95);
- box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
- }
+ 100% {
+ transform: scale(0.95);
+ box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
+ }
}
/* Workaround for Needle not handling menu placement of dropdowns on modals */
@@ -247,14 +241,14 @@
/* End workaround */
/* Workaround for cleaning the Gantt chart UI */
-.gantt-wrapper > div > div:first-child > div:first-child > div:first-child > div> div:not(:first-child) {
+.gantt-wrapper > div > div:first-child > div:first-child > div:first-child > div > div:not(:first-child) {
display: none;
}
-.gantt-wrapper > div > div > div > div> div> div> div:not(:first-child) {
+.gantt-wrapper > div > div > div > div > div > div > div:not(:first-child) {
display: none;
}
/* End Gantt chart workaround */
.markdown-widget a {
text-decoration: underline;
-}
\ No newline at end of file
+}
diff --git a/release-notes.md b/release-notes.md
index 5546ba3cd..68200808b 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -1,11 +1,19 @@
-## NeoDash 2.4.2
-This is a release with a large amount of quality of life improvements, as well as some new features:
-
-- Visualize graphs in 3D with the new 3D graph report. [#737](https://github.com/neo4j-labs/neodash/pull/737)
-- Improved dashboard management sidebar and handling of drafts. [#734](https://github.com/neo4j-labs/neodash/pull/734)
-- Added parameter select setting for autopopulating first selector value. [#746](https://github.com/neo4j-labs/neodash/pull/746)
-- Improved UX for editing page names & dashboard titles. [#743](https://github.com/neo4j-labs/neodash/pull/743)
-- Unified common settings for each report type. [#724](https://github.com/neo4j-labs/neodash/pull/724)
-- Title of the browser tab NeoDash runs on is now automatically set to the dashboard name. [#708](https://github.com/neo4j-labs/neodash/pull/708)
-- Fixed issue where invisible table columns were not handled correctly. [#695](https://github.com/neo4j-labs/neodash/pull/695)
-- Miscellaneous bug fixes, style improvements & stability fixes. [#744](https://github.com/neo4j-labs/neodash/pull/744)
+## NeoDash 2.4.9
+This release adds some minor changes to documentation and implements some community contributions.
+- Added notice about project evolution: [#967](https://github.com/neo4j-labs/neodash/pull/967)
+- Added community contributions and bug fixes:
+[#967](https://github.com/neo4j-labs/neodash/pull/967)
+[#894](https://github.com/neo4j-labs/neodash/pull/894)
+[#822](https://github.com/neo4j-labs/neodash/pull/822)
+[#951](https://github.com/neo4j-labs/neodash/pull/951)
+[#946](https://github.com/neo4j-labs/neodash/pull/946)
+[#944](https://github.com/neo4j-labs/neodash/pull/944)
+[#943](https://github.com/neo4j-labs/neodash/pull/943)
+[#938](https://github.com/neo4j-labs/neodash/pull/938)
+[#935](https://github.com/neo4j-labs/neodash/pull/935)
+[#918](https://github.com/neo4j-labs/neodash/pull/918)
+[#908](https://github.com/neo4j-labs/neodash/pull/908)
+[#906](https://github.com/neo4j-labs/neodash/pull/906)
+[#902](https://github.com/neo4j-labs/neodash/pull/902)
+[#895](https://github.com/neo4j-labs/neodash/pull/895)
+[#893](https://github.com/neo4j-labs/neodash/pull/893)
\ No newline at end of file
diff --git a/scripts/config-entrypoint.sh b/scripts/config-entrypoint.sh
index 65a7f9d4f..3e2a063ec 100644
--- a/scripts/config-entrypoint.sh
+++ b/scripts/config-entrypoint.sh
@@ -21,9 +21,23 @@ echo " \
\"standaloneLoadFromOtherDatabases\": ${standaloneLoadFromOtherDatabases:=false}, \
\"standaloneMultiDatabase\": ${standaloneMultiDatabase:=false}, \
\"standaloneDatabaseList\": \"${standaloneDatabaseList:='neo4j'}\", \
+ \"standalonePasswordWarningHidden\": ${standalonePasswordWarningHidden:=false}, \
\"loggingMode\": \"${loggingMode:='0'}\", \
\"loggingDatabase\": \"${loggingDatabase:='logs'}\", \
\"customHeader\": \"${customHeader:=}\" \
}" > /usr/share/nginx/html/config.json
-echo "${styleConfigJson:={\}}" > /usr/share/nginx/html/style.config.json
+echo " \
+ { \
+ \"DASHBOARD_HEADER_BRAND_LOGO\": \"${DASHBOARD_HEADER_BRAND_LOGO:=}\", \
+ \"DASHBOARD_HEADER_COLOR\" : \"${DASHBOARD_HEADER_COLOR:=}\", \
+ \"DASHBOARD_HEADER_BUTTON_COLOR\" : \"${DASHBOARD_HEADER_BUTTON_COLOR:=}\", \
+ \"DASHBOARD_HEADER_TITLE_COLOR\" : \"${DASHBOARD_HEADER_TITLE_COLOR:=}\", \
+ \"DASHBOARD_PAGE_LIST_COLOR\" : \"${DASHBOARD_PAGE_LIST_COLOR:=}\", \
+ \"DASHBOARD_PAGE_LIST_ACTIVE_COLOR\": \"${DASHBOARD_PAGE_LIST_ACTIVE_COLOR:=}\", \
+ \"style\": { \
+ \"--palette-light-neutral-bg-weak\": \"${STYLE_PALETTE_LIGHT_NEUTRAL_BG_WEAK:=}\" \
+ } \
+}" > /usr/share/nginx/html/style.config.json
+
+
diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts
index 0b556f2dc..b8fe3d063 100644
--- a/src/application/ApplicationActions.ts
+++ b/src/application/ApplicationActions.ts
@@ -178,7 +178,7 @@ export const setStandaloneEnabled = (
export const SET_STANDALONE_MODE = 'APPLICATION/SET_STANDALONE_MODE';
export const setStandaloneMode = (standalone: boolean) => ({
- type: SET_STANDALONE_ENABLED,
+ type: SET_STANDALONE_MODE,
payload: { standalone },
});
diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts
index 6172d2709..e662ade63 100644
--- a/src/application/ApplicationReducer.ts
+++ b/src/application/ApplicationReducer.ts
@@ -2,7 +2,14 @@
* Reducers define changes to the application state when a given action is taken.
*/
-import { HARD_RESET_CARD_SETTINGS, UPDATE_ALL_SELECTIONS, UPDATE_FIELDS, UPDATE_SCHEMA } from '../card/CardActions';
+import {
+ HARD_RESET_CARD_SETTINGS,
+ TOGGLE_CARD_SETTINGS,
+ UPDATE_ALL_SELECTIONS,
+ UPDATE_FIELDS,
+ UPDATE_SCHEMA,
+ UPDATE_SELECTION,
+} from '../card/CardActions';
import { DEFAULT_NEO4J_URL } from '../config/ApplicationConfig';
import { SET_DASHBOARD, SET_DASHBOARD_UUID } from '../dashboard/DashboardActions';
import { UPDATE_DASHBOARD_SETTING } from '../settings/SettingsActions';
@@ -80,7 +87,10 @@ export const applicationReducer = (state = initialState, action: { type: any; pa
UPDATE_ALL_SELECTIONS,
UPDATE_FIELDS,
SET_DASHBOARD_UUID,
+ TOGGLE_CARD_SETTINGS,
+ UPDATE_SELECTION,
];
+
if (!state.draft && !NON_TRANSFORMATIVE_ACTIONS.includes(type)) {
state = update(state, { draft: true });
return state;
diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts
index fc880fd3a..6af4b6339 100644
--- a/src/application/ApplicationThunks.ts
+++ b/src/application/ApplicationThunks.ts
@@ -14,7 +14,6 @@ import { createNotificationThunk } from '../page/PageThunks';
import { runCypherQuery } from '../report/ReportQueryRunner';
import {
setPageNumberThunk,
- updateParametersToNeo4jTypeThunk,
updateGlobalParametersThunk,
updateSessionParameterThunk,
} from '../settings/SettingsThunks';
@@ -259,7 +258,9 @@ export const handleSharedDashboardsThunk = () => (dispatch: any) => {
const skipConfirmation = urlParams.get('skipConfirmation') == 'Yes';
const dashboardDatabase = urlParams.get('dashboardDatabase');
- dispatch(setStandaloneDashboardDatabase(dashboardDatabase));
+ if (dashboardDatabase) {
+ dispatch(setStandaloneDashboardDatabase(dashboardDatabase));
+ }
if (urlParams.get('credentials')) {
setWelcomeScreenOpen(false);
const connection = decodeURIComponent(urlParams.get('credentials'));
@@ -269,32 +270,6 @@ export const handleSharedDashboardsThunk = () => (dispatch: any) => {
const database = connection.split('@')[1].split(':')[0];
const url = connection.split('@')[1].split(':')[1];
const port = connection.split('@')[1].split(':')[2];
- // if (url == password) {
- // // Special case where a connect link is generated without a password.
- // // Here, the format is parsed incorrectly and we open the connection window instead.
- // dispatch(setConnectionProperties(protocol, url, port, database, username.split('@')[0], ''));
- // dispatch(
- // setShareDetailsFromUrl(
- // type,
- // id,
- // standalone,
- // protocol,
- // url,
- // port,
- // database,
- // username.split('@')[0],
- // '',
- // dashboardDatabase,
- // true
- // )
- // );
- // setDashboardToLoadAfterConnecting(id);
- // window.history.pushState({}, document.title, window.location.pathname);
- // dispatch(setConnectionModalOpen(true));
- // dispatch(setWelcomeScreenOpen(false));
- // // window.history.pushState({}, document.title, "/");
- // return;
- // }
dispatch(setConnectionModalOpen(false));
dispatch(
@@ -325,7 +300,7 @@ export const handleSharedDashboardsThunk = () => (dispatch: any) => {
setShareDetailsFromUrl(
type,
id,
- undefined,
+ standalone,
undefined,
undefined,
undefined,
@@ -363,6 +338,8 @@ export const onConfirmLoadSharedDashboardThunk = () => (dispatch: any, getState:
if (shareDetails.dashboardDatabase) {
dispatch(setStandaloneDashboardDatabase(shareDetails.dashboardDatabase));
+ } else if (!state.application.standaloneDashboardDatabase) {
+ // No standalone dashboard database configured, fall back to default
dispatch(setStandaloneDashboardDatabase(shareDetails.database));
}
if (shareDetails.url) {
@@ -381,6 +358,7 @@ export const onConfirmLoadSharedDashboardThunk = () => (dispatch: any, getState:
}
if (shareDetails.standalone == true) {
dispatch(setStandaloneMode(true));
+ localStorage.setItem('standaloneShared', 'true'); // EDGE CASE: redirect SSO removes the shareDetails when redirecting
}
dispatch(resetShareDetails());
} catch (e) {
@@ -451,7 +429,13 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState:
dispatch(setSSOEnabled(config.ssoEnabled, state.application.cachedSSODiscoveryUrl));
dispatch(setSSOProviders(config.ssoProviders));
+ // Check if we are in standalone mode
+ // const standaloneShared = localStorage.getItem('standaloneShared') == 'true'; // EDGE case: from url param it could happen that we lose the value due to SSO redirect
const { standalone } = config;
+ // || standaloneShared;
+
+ // if a dashboard database was previously set, remember to use it.
+ const dashboardDatabase = state.application.standaloneDashboardDatabase;
dispatch(
setStandaloneEnabled(
standalone,
@@ -460,7 +444,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState:
config.standalonePort,
config.standaloneDatabase,
config.standaloneDashboardName,
- config.standaloneDashboardDatabase,
+ dashboardDatabase || config.standaloneDashboardDatabase,
config.standaloneDashboardURL,
config.standaloneUsername,
config.standalonePassword,
@@ -471,6 +455,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState:
config.standaloneDatabaseList
)
);
+ localStorage.removeItem('standaloneShared');
dispatch(setLoggingMode(config.loggingMode));
dispatch(setLoggingDatabase(config.loggingDatabase));
@@ -499,8 +484,6 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState:
);
}
}
- // At the load of a dashboard, we want to ensure correct casting types
- dispatch(updateParametersToNeo4jTypeThunk());
// SSO - specific case starts here.
if (state.application.waitForSSO) {
@@ -642,8 +625,8 @@ export const initializeApplicationAsStandaloneThunk =
} else {
dispatch(setDashboardToLoadAfterConnecting(`name:${config.standaloneDashboardName}`));
}
-
dispatch(setParametersToLoadAfterConnecting(paramsToSetAfterConnecting));
+ dispatch(updateGlobalParametersThunk(paramsToSetAfterConnecting));
if (clearNotificationAfterLoad) {
dispatch(clearNotification());
diff --git a/src/card/settings/CardSettingsContent.tsx b/src/card/settings/CardSettingsContent.tsx
index e2e0e4126..c02f1d339 100644
--- a/src/card/settings/CardSettingsContent.tsx
+++ b/src/card/settings/CardSettingsContent.tsx
@@ -105,6 +105,7 @@ const NeoCardSettingsContent = ({
return (
{
headers.forEach((header) => {
// Parse value
let value = row[header];
- if (value && value.low) {
+ if (value && 'low' in value) {
value = value.low;
}
- csv += JSON.stringify(value).replaceAll(',', ';');
- csv += headers.indexOf(header) < headers.length - 1 ? ', ' : '';
+ csv += `${JSON.stringify(value)}`;
+ csv += headers.indexOf(header) < headers.length - 1 ? ',' : '';
});
csv += '\n';
});
-
- const file = new Blob([csv], { type: 'text/plain' });
+ const file = new Blob([`\ufeff${csv}`], { type: 'text/plain;charset=utf8' });
element.href = URL.createObjectURL(file);
element.download = 'table.csv';
document.body.appendChild(element); // Required for this to work in FireFox
@@ -219,8 +218,14 @@ export function replaceDashboardParameters(str, parameters) {
let param = _.replace(`$`, '').trim();
let val = parameters?.[param] || null;
let type = getRecordType(val);
- let valueRender = type === 'string' || type == 'link' ? val : RenderSubValue(val);
- return valueRender;
+
+ // Arrays weren't playing nicely with RenderSubValue(). Each object would be passed separately and return [oject Object].
+ if (type === 'string' || type == 'link') {
+ return val;
+ } else if (type === 'array') {
+ return RenderSubValue(val.join(', '));
+ }
+ return RenderSubValue(val);
};
let newString = str.replace(rx, parameterElementReplacer).replace(rxSimple, parameterSimpleReplacer);
@@ -410,7 +415,7 @@ export function isCastableToNeo4jDate(value: object) {
return false;
}
let keys = Object.keys(value);
- return keys.length == 3 && keys.includes('day') && keys.includes('month') && keys.includes('year');
+ return keys.includes('day') && keys.includes('month') && keys.includes('year');
}
/**
@@ -420,7 +425,7 @@ export function isCastableToNeo4jDate(value: object) {
*/
export function castToNeo4jDate(value: object) {
if (isCastableToNeo4jDate(value)) {
- return new Neo4jDate(value.year, value.month, value.day);
+ return new Neo4jDate(toNumber(value.year), toNumber(value.month), toNumber(value.day));
}
throw new Error(`Invalid input for castToNeo4jDate: ${value}`);
}
diff --git a/src/chart/bar/BarChart.tsx b/src/chart/bar/BarChart.tsx
index 4d754c399..cd3a5582f 100644
--- a/src/chart/bar/BarChart.tsx
+++ b/src/chart/bar/BarChart.tsx
@@ -28,6 +28,9 @@ const NeoBarChart = (props: ChartProps) => {
const padding = settings.padding ? settings.padding : 0.25;
const innerPadding = settings.innerPadding ? settings.innerPadding : 0;
const expandHeightForLegend = settings.expandHeightForLegend ? settings.expandHeightForLegend : false;
+ const displayYAxis = settings.displayYAxis ?? true;
+ const displayYGridLines = settings.displayYGridLines ?? true;
+
const actionsRules =
extensionEnabled(props.extensions, 'actions') && props.settings && props.settings.actionsRules
? props.settings.actionsRules
@@ -272,7 +275,7 @@ const NeoBarChart = (props: ChartProps) => {
return { width: this.offsetWidth, height: this.offsetHeight };
};
- const extraProperties = positionLabel ? { barComponent: BarComponent } : {};
+ const extraProperties = positionLabel !== 'off' ? { barComponent: BarComponent } : {};
const canvas = data.length > 30;
const BarChartComponent = canvas ? ResponsiveBarCanvas : ResponsiveBar;
@@ -408,11 +411,16 @@ const NeoBarChart = (props: ChartProps) => {
tickPadding: 5,
tickRotation: labelRotation,
}}
- axisLeft={{
- tickSize: 5,
- tickPadding: 5,
- tickRotation: 0,
- }}
+ axisLeft={
+ displayYAxis
+ ? {
+ tickSize: 5,
+ tickPadding: 5,
+ tickRotation: 0,
+ }
+ : null
+ }
+ enableGridY={displayYGridLines}
labelSkipWidth={labelSkipWidth}
labelSkipHeight={labelSkipHeight}
labelTextColor={{ from: 'color', modifiers: [['darker', 1.6]] }}
diff --git a/src/chart/graph/component/GraphEntityInspectionTable.tsx b/src/chart/graph/component/GraphEntityInspectionTable.tsx
index 62801204f..562fbbaec 100644
--- a/src/chart/graph/component/GraphEntityInspectionTable.tsx
+++ b/src/chart/graph/component/GraphEntityInspectionTable.tsx
@@ -2,16 +2,18 @@ import React from 'react';
import ShowMoreText from 'react-show-more-text';
import { Checkbox, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
import { TextLink } from '@neo4j-ndl/react';
+// import DOMPurify from 'dompurify';
export const formatProperty = (property) => {
- if (property.startsWith('http://') || property.startsWith('https://')) {
+ const str = property?.toString() || '';
+ if (str.startsWith('http://') || str.startsWith('https://')) {
return (
-
- {property}
+
+ {str}
);
}
- return property;
+ return str;
};
/**
@@ -88,7 +90,7 @@ export const GraphEntityInspectionTable = ({
{key}
- {formatProperty(entity && entity.properties[key].toString())}
+ {formatProperty(entity?.properties[key])}
{checklistEnabled ? (
diff --git a/src/chart/graph/util/RecordUtils.ts b/src/chart/graph/util/RecordUtils.ts
index f432e6596..f2d6dd4d6 100644
--- a/src/chart/graph/util/RecordUtils.ts
+++ b/src/chart/graph/util/RecordUtils.ts
@@ -1,6 +1,6 @@
import { evaluateRulesOnNode, evaluateRulesOnLink } from '../../../extensions/styling/StyleRuleEvaluator';
import { extractNodePropertiesFromRecords, mergeNodePropsFieldsLists } from '../../../report/ReportRecordProcessing';
-import { valueIsArray, valueIsNode, valueIsRelationship, valueIsPath } from '../../ChartUtils';
+import { valueIsArray, valueIsNode, valueIsRelationship, valueIsPath, toNumber } from '../../ChartUtils';
import { GraphChartVisualizationProps } from '../GraphChartVisualization';
import { assignCurvatureToLink } from './RelUtils';
import { isNode } from 'neo4j-driver-core/lib/graph-types.js';
@@ -49,7 +49,9 @@ function extractGraphEntitiesFromField(
nodes[value.identity.low] = {
id: value.identity.low,
labels: value.labels,
- size: value.properties[nodeSizeProperty] ? value.properties[nodeSizeProperty] : defaultNodeSize,
+ size: !Number.isNaN(value.properties[nodeSizeProperty])
+ ? toNumber(value.properties[nodeSizeProperty])
+ : defaultNodeSize,
properties: value.properties,
mainLabel: value.labels[value.labels.length - 1],
};
@@ -67,7 +69,10 @@ function extractGraphEntitiesFromField(
source: value.start.low,
target: value.end.low,
type: value.type,
- width: value.properties[relWidthProperty] ? value.properties[relWidthProperty] : defaultRelWidth,
+ width:
+ value.properties[relWidthProperty] !== undefined && !Number.isNaN(value.properties[relWidthProperty])
+ ? toNumber(value.properties[relWidthProperty])
+ : defaultRelWidth,
color: value.properties[relColorProperty] ? value.properties[relColorProperty] : defaultRelColor,
properties: value.properties,
});
diff --git a/src/chart/markdown/MarkdownChart.tsx b/src/chart/markdown/MarkdownChart.tsx
index d373b0da5..a6958edcc 100644
--- a/src/chart/markdown/MarkdownChart.tsx
+++ b/src/chart/markdown/MarkdownChart.tsx
@@ -1,13 +1,40 @@
import React from 'react';
import { ChartProps } from '../Chart';
import ReactMarkdown from 'react-markdown';
-import gfm from 'remark-gfm';
+import remarkGfm from 'remark-gfm';
+import URI from 'urijs';
import { replaceDashboardParameters } from '../ChartUtils';
+// Sanitizes URIs
+const transformUri = (uri: string): string | undefined => {
+ const parsedUri = URI(uri);
+ if (parsedUri.protocol() === 'http' || parsedUri.protocol() === 'https') {
+ return parsedUri.toString(); // Convert URI object back to string
+ }
+ return undefined; // Return undefined to skip rendering of potentially unsafe URLs
+};
+
+// Define custom components for Markdown elements
+const CustomTable = ({ _, ...props }) => ;
+const CustomTh = ({ _, ...props }) => ;
+const CustomTd = ({ _, ...props }) => ;
+const CustomATag = ({ _, href, ...props }) => (
+ // Apply URI transformation right in the anchor element for additional security
+
+);
+
/**
* Renders Markdown text provided by the user.
*/
const NeoMarkdownChart = (props: ChartProps) => {
+ // Define custom components for Markdown elements
+ const components = {
+ table: CustomTable,
+ th: CustomTh,
+ td: CustomTd,
+ a: CustomATag,
+ };
+
// Records are overridden to be a single element array with a field called 'input'.
const { records } = props;
const parameters = props.parameters ? props.parameters : {};
@@ -17,13 +44,18 @@ const NeoMarkdownChart = (props: ChartProps) => {
: true;
const markdown = records[0].input;
const modifiedMarkdown = replaceGlobalParameters ? replaceDashboardParameters(markdown, parameters) : markdown;
- // TODO: we should check if the gfm plugin has an impact on the standard security provided by ReactMarkdown
return (
-
+
+
);
};
diff --git a/src/chart/parameter/ParameterSelectionChart.tsx b/src/chart/parameter/ParameterSelectionChart.tsx
index bb48334b0..56a67ea09 100644
--- a/src/chart/parameter/ParameterSelectionChart.tsx
+++ b/src/chart/parameter/ParameterSelectionChart.tsx
@@ -94,6 +94,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
compatibilityMode={compatibilityMode}
multiSelector={multiSelector}
manualParameterSave={manualParameterSave}
+ autoSort={true}
/>
);
} else if (type == 'Relationship Property') {
@@ -112,6 +113,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
compatibilityMode={compatibilityMode}
multiSelector={multiSelector}
manualParameterSave={manualParameterSave}
+ autoSort={true}
/>
);
} else if (type == 'Date Picker') {
@@ -147,6 +149,7 @@ export const NeoParameterSelectionChart = (props: ChartProps) => {
compatibilityMode={compatibilityMode}
multiSelector={multiSelector}
manualParameterSave={manualParameterSave}
+ autoSort={false}
/>
);
}
diff --git a/src/chart/parameter/component/DateParameterSelect.tsx b/src/chart/parameter/component/DateParameterSelect.tsx
index ab6db9187..25d2e0f65 100644
--- a/src/chart/parameter/component/DateParameterSelect.tsx
+++ b/src/chart/parameter/component/DateParameterSelect.tsx
@@ -4,7 +4,6 @@ import NeoDatePicker from '../../../component/field/DateField';
import dayjs from 'dayjs';
import { Date as Neo4jDate } from 'neo4j-driver-core/lib/temporal-types.js';
import { isCastableToNeo4jDate, isEmptyObject } from '../../ChartUtils';
-import { debounce } from '@mui/material';
function castPropsToBoltDate(dict) {
if (isEmptyObject(dict)) {
diff --git a/src/chart/parameter/component/NodePropertyParameterSelect.tsx b/src/chart/parameter/component/NodePropertyParameterSelect.tsx
index 48d3006d1..4972ff225 100644
--- a/src/chart/parameter/component/NodePropertyParameterSelect.tsx
+++ b/src/chart/parameter/component/NodePropertyParameterSelect.tsx
@@ -103,13 +103,15 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
let valDisplayReference = manualParameterSave ? paramValueDisplayLocal : props.parameterDisplayValue;
// Multiple and new entry
if (isMulti && inputValue !== null && newDisplay !== null && inputValue.length < newDisplay.length) {
- newValue = Array.isArray(valReference) ? [...valReference] : [valReference];
+ newValue = Array.isArray(valReference)
+ ? [...valReference]
+ : valReference && valReference !== null
+ ? [valReference]
+ : [];
const newDisplayValue = [...newDisplay].slice(-1)[0];
-
let val = extraRecords.filter((r) => r._fields[displayValueRowIndex].toString() == newDisplayValue)[0]._fields[
realValueRowIndex
];
-
if (newValue.low) {
newValue.push(toNumber(val));
} else {
@@ -119,7 +121,8 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
newValue = extraRecords.filter((r) => (r?._fields?.[displayValueRowIndex]?.toString() || null) == newDisplay)[0]
._fields[realValueRowIndex];
- newValue = newValue.low ? toNumber(newValue) : RenderSubValue(newValue);
+ newValue =
+ (newValue.low && newValue.low != null) || newValue.low === 0 ? toNumber(newValue) : RenderSubValue(newValue);
} else {
let ele = valDisplayReference.filter((x) => !newDisplay.includes(x))[0];
newValue = [...valReference];
@@ -127,10 +130,8 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
}
newDisplay = newDisplay.low ? toNumber(newDisplay) : RenderSubValue(newDisplay);
-
setInputDisplayText(isMulti ? '' : newDisplay);
setInputValue(newDisplay);
-
handleParametersUpdate(newValue, newDisplay, manualParameterSave);
};
@@ -164,13 +165,14 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
/>
);
}
-
+ let options = extraRecords?.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)');
+ options = props.autoSort ? options.sort() : options;
return (
r?._fields?.[displayValueRowIndex] || '(no data)').sort()}
+ options={options}
disabled={disabled}
limitTags={multiSelectLimit}
style={{
@@ -179,7 +181,7 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
marginLeft: '15px',
marginTop: '5px',
}}
- inputValue={inputDisplayText || ''}
+ inputValue={inputDisplayText.toString() || ''}
onInputChange={(event, value) => {
setInputDisplayText(value);
debouncedQueryCallback(props.query, { input: `${value}`, ...allParameters }, setExtraRecords);
@@ -198,7 +200,8 @@ const NodePropertyParameterSelectComponent = (props: ParameterSelectProps) => {
if (autoSelectFirstValue && paramValueDisplayLocal == '') {
debouncedQueryCallback(props.query, { input: '', ...allParameters }, (records) => {
if (records && records.length > 0 && records[0] && records[0]._fields) {
- const values = records?.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)').sort();
+ let values = records?.map((r) => r?._fields?.[displayValueRowIndex] || '(no data)');
+ values = props.autoSort ? values.sort() : values;
setExtraRecords(records);
propagateSelection(undefined, values[0]);
}
diff --git a/src/chart/parameter/component/ParameterSelect.ts b/src/chart/parameter/component/ParameterSelect.ts
index 2d9cf9966..54e9f2a67 100644
--- a/src/chart/parameter/component/ParameterSelect.ts
+++ b/src/chart/parameter/component/ParameterSelect.ts
@@ -62,4 +62,8 @@ export interface ParameterSelectProps {
* Add the possibility for manual selection confirmation
*/
manualParameterSave?: boolean;
+ /**
+ * Pass true if results should be sorted automatically
+ */
+ autoSort?: boolean;
}
diff --git a/src/chart/parameter/component/QueryParameterSelect.tsx b/src/chart/parameter/component/QueryParameterSelect.tsx
index e4fdeb807..125c10532 100644
--- a/src/chart/parameter/component/QueryParameterSelect.tsx
+++ b/src/chart/parameter/component/QueryParameterSelect.tsx
@@ -17,6 +17,7 @@ const QueryParameterSelectComponent = (props: ParameterSelectProps) => {
allParameters={props.allParameters}
compatibilityMode={props.compatibilityMode}
multiSelector={props.multiSelector}
+ autoSort={props.autoSort}
/>
);
};
diff --git a/src/chart/table/TableActionsHelper.ts b/src/chart/table/TableActionsHelper.ts
index 7b946df2e..1dc3a4508 100644
--- a/src/chart/table/TableActionsHelper.ts
+++ b/src/chart/table/TableActionsHelper.ts
@@ -15,17 +15,17 @@ export const getCheckboxes = (actionsRules, rows, getGlobalParameter) => {
// If the parameter is an array (to be expected), iterate over it to find the rows to check.
if (Array.isArray(values)) {
values.forEach((value) => {
- rows.forEach((row, index) => {
+ rows.forEach((row) => {
if (row[fieldName] == value) {
- selection.push(index);
+ selection.push(row.id);
}
});
});
} else {
// Else (special case), still check the row if it's a single value parameter.
- rows.forEach((row, index) => {
+ rows.forEach((row) => {
if (row[fieldName] == values) {
- selection.push(index);
+ selection.push(row.id);
}
});
}
@@ -35,7 +35,8 @@ export const getCheckboxes = (actionsRules, rows, getGlobalParameter) => {
export const updateCheckBoxes = (actionsRules, rows, selection, setGlobalParameter) => {
if (hasCheckboxes(actionsRules)) {
- const selectedRows = rows.filter((_, i) => selection.includes(i));
+ const selectedRows = rows.filter((row) => selection.includes(row.id));
+ console.log(selectedRows);
let rules = actionsRules.filter((rule) => rule.condition && rule.condition == 'rowCheck');
rules.forEach((rule) => {
const parameterValues = selectedRows.map((row) => row[rule.value]).filter((v) => v !== undefined);
diff --git a/src/chart/table/TableChart.tsx b/src/chart/table/TableChart.tsx
index 4f5f08e3a..57e579207 100644
--- a/src/chart/table/TableChart.tsx
+++ b/src/chart/table/TableChart.tsx
@@ -3,6 +3,7 @@ import { DataGrid, GridColumnVisibilityModel } from '@mui/x-data-grid';
import { ChartProps } from '../Chart';
import {
evaluateRulesOnDict,
+ evaluateSingleRuleOnDict,
generateClassDefinitionsBasedOnRules,
useStyleRules,
} from '../../extensions/styling/StyleRuleEvaluator';
@@ -24,8 +25,6 @@ import Button from '@mui/material/Button';
import { extensionEnabled } from '../../utils/ReportUtils';
import { getCheckboxes, hasCheckboxes, updateCheckBoxes } from './TableActionsHelper';
-const TABLE_HEADER_HEIGHT = 32;
-const TABLE_FOOTER_HEIGHT = 62;
const TABLE_ROW_HEIGHT = 52;
const HIDDEN_COLUMN_PREFIX = '__';
const theme = createTheme({
@@ -46,11 +45,9 @@ function renderAsButtonWrapper(renderer) {
return <>>;
}
return (
- {`${outputValue}`}
+
+ {outputValue}
+
);
};
}
@@ -73,6 +70,7 @@ export const generateSafeColumnKey = (key) => {
export const NeoTableChart = (props: ChartProps) => {
const transposed = props.settings && props.settings.transposed ? props.settings.transposed : false;
+ const wrapContent = props.settings && props.settings.wrapContent ? props.settings.wrapContent : false;
const allowDownload =
props.settings && props.settings.allowDownload !== undefined ? props.settings.allowDownload : false;
@@ -93,7 +91,6 @@ export const NeoTableChart = (props: ChartProps) => {
const useStyles = generateClassDefinitionsBasedOnRules(styleRules);
const classes = useStyles();
const tableRowHeight = compact ? TABLE_ROW_HEIGHT / 2 : TABLE_ROW_HEIGHT;
- const pageSizeReducer = compact ? 3 : 1;
const columnWidthsType =
props.settings && props.settings.columnWidthsType ? props.settings.columnWidthsType : 'Relative (%)';
@@ -185,7 +182,9 @@ export const NeoTableChart = (props: ChartProps) => {
Object.assign(
{ id: i, Field: key },
...records.map((record, j) => ({
- [`${record._fields[0]}_${j + 1}`]: RenderSubValue(record._fields[i + 1]),
+ // Note the true here is for the rendered to know we are inside a transposed table
+ // It will be needed for rendering the records properly, if they are arrays
+ [`${record._fields[0]}_${j + 1}`]: RenderSubValue(record._fields[i + 1], true),
}))
)
);
@@ -208,12 +207,64 @@ export const NeoTableChart = (props: ChartProps) => {
);
});
- const availableRowHeight = (props.dimensions.height - TABLE_HEADER_HEIGHT - TABLE_FOOTER_HEIGHT) / tableRowHeight;
- const tablePageSize = compact
- ? Math.round(availableRowHeight) - pageSizeReducer
- : Math.floor(availableRowHeight) - pageSizeReducer;
-
const pageNames = getPageNumbersAndNamesList();
+ const customStyles = { '&.MuiDataGrid-root .MuiDataGrid-footerContainer > div': { marginTop: '0px' } };
+
+ const commonGridProps = {
+ key: 'tableKey',
+ columnHeaderHeight: 32,
+ rowHeight: tableRowHeight,
+ autoPageSize: true,
+ rows: rows,
+ columns: columns,
+ columnVisibilityModel: columnVisibilityModel,
+ onColumnVisibilityModelChange: (newModel) => setColumnVisibilityModel(newModel),
+ onCellClick: (e) => performActionOnElement(e, actionsRules, { ...props, pageNames: pageNames }, 'Click', 'Table'),
+ onCellDoubleClick: (e) => {
+ let rules = getRule(e, actionsRules, 'doubleClick');
+ if (rules !== null) {
+ rules.forEach((rule) => executeActionRule(rule, e, { ...props, pageNames: pageNames }, 'table'));
+ } else {
+ setNotificationOpen(true);
+ navigator.clipboard.writeText(e.value);
+ }
+ },
+ checkboxSelection: hasCheckboxes(actionsRules),
+ rowSelectionModel: getCheckboxes(actionsRules, rows, props.getGlobalParameter),
+ onRowSelectionModelChange: (selection) => updateCheckBoxes(actionsRules, rows, selection, props.setGlobalParameter),
+ disableRowSelectionOnClick: true,
+ components: {
+ ColumnSortedDescendingIcon: () => <>>,
+ ColumnSortedAscendingIcon: () => <>>,
+ },
+ // TODO: if mixing and matching row and cell styling, row rules MUST be set first or will not populate correctly
+ getRowClassName: (params) => {
+ return ['row color', 'row text color']
+ .map((e) => {
+ return `rule${evaluateRulesOnDict(params.row, styleRules, [e])}`;
+ })
+ .join(' ');
+ },
+ getCellClassName: (params) => {
+ return ['cell color', 'cell text color']
+ .map((e) => {
+ let trueRulesList = [''];
+ let trueRule;
+ for (const [index, rule] of styleRules.entries()) {
+ if (rule.targetField) {
+ if (rule.targetField === params.field) {
+ trueRule = `rule${evaluateSingleRuleOnDict({ [rule.field]: params.row[rule.field] }, rule, index, [e])}`;
+ }
+ } else {
+ trueRule = `rule${evaluateSingleRuleOnDict({ [params.field]: params.value }, rule, index, [e])}`;
+ }
+ trueRulesList.push(trueRule);
+ }
+ return trueRulesList.join(' ');
+ })
+ .join(' ');
+ },
+ };
return (
@@ -249,7 +300,7 @@ export const NeoTableChart = (props: ChartProps) => {
downloadCSV(rows);
}}
aria-label='download csv'
- className='n-absolute n-z-10 n-bottom-7 n-left-1'
+ className='n-absolute n-z-10 n-bottom-2 n-left-1'
clean
>
@@ -259,53 +310,18 @@ export const NeoTableChart = (props: ChartProps) => {
<>>
)}
- setColumnVisibilityModel(newModel)}
- onCellClick={(e) =>
- performActionOnElement(e, actionsRules, { ...props, pageNames: pageNames }, 'Click', 'Table')
- }
- onCellDoubleClick={(e) => {
- let rules = getRule(e, actionsRules, 'doubleClick');
- if (rules !== null) {
- rules.forEach((rule) => executeActionRule(rule, e, { ...props, pageNames: pageNames }, 'table'));
- } else {
- setNotificationOpen(true);
- navigator.clipboard.writeText(e.value);
- }
- }}
- checkboxSelection={hasCheckboxes(actionsRules)}
- selectionModel={getCheckboxes(actionsRules, rows, props.getGlobalParameter)}
- onSelectionModelChange={(selection) =>
- updateCheckBoxes(actionsRules, rows, selection, props.setGlobalParameter)
- }
- pageSize={tablePageSize > 0 ? tablePageSize : 5}
- rowsPerPageOptions={rows.length < 5 ? [rows.length, 5] : [5]}
- disableSelectionOnClick
- components={{
- ColumnSortedDescendingIcon: () => <>>,
- ColumnSortedAscendingIcon: () => <>>,
- }}
- getRowClassName={(params) => {
- return ['row color', 'row text color']
- .map((e) => {
- return `rule${evaluateRulesOnDict(params.row, styleRules, [e])}`;
- })
- .join(' ');
- }}
- getCellClassName={(params) => {
- return ['cell color', 'cell text color']
- .map((e) => {
- return `rule${evaluateRulesOnDict({ [params.field]: params.value }, styleRules, [e])}`;
- })
- .join(' ');
- }}
- />
+ {wrapContent ? (
+ 'auto'}
+ sx={{
+ ...customStyles,
+ '&.MuiDataGrid-root .MuiDataGrid-cell': { wordBreak: 'break-word' },
+ }}
+ />
+ ) : (
+
+ )}
);
diff --git a/src/config/ReportConfig.tsx b/src/config/ReportConfig.tsx
index e9270c314..be72ffb9d 100644
--- a/src/config/ReportConfig.tsx
+++ b/src/config/ReportConfig.tsx
@@ -46,6 +46,12 @@ const _REPORT_TYPES = {
values: [true, false],
default: false,
},
+ wrapContent: {
+ label: 'Wrap overflowing content',
+ type: SELECTION_TYPES.LIST,
+ values: [true, false],
+ default: false,
+ },
columnWidthsType: {
label: 'Column Widths Specification',
type: SELECTION_TYPES.LIST,
@@ -415,27 +421,17 @@ const _REPORT_TYPES = {
type: SELECTION_TYPES.NUMBER,
default: 0.25,
},
- expandHeightForLegend: {
- label: 'Expand Height For Legend',
+ displayYAxis: {
+ label: 'Display Y axis',
type: SELECTION_TYPES.LIST,
values: [true, false],
- default: false,
- },
- innerPadding: {
- label: 'Inner Padding',
- type: SELECTION_TYPES.NUMBER,
- default: 0,
+ default: true,
},
- legendPosition: {
- label: 'Legend Position',
+ displayYGridLines: {
+ label: 'Display Y grid lines',
type: SELECTION_TYPES.LIST,
- values: ['Horizontal', 'Vertical'],
- default: 'Vertical',
- },
- padding: {
- label: 'Padding',
- type: SELECTION_TYPES.NUMBER,
- default: 0.25,
+ values: [true, false],
+ default: true,
},
},
},
diff --git a/src/dashboard/DashboardReducer.ts b/src/dashboard/DashboardReducer.ts
index c4bd865b8..b2a16c35e 100644
--- a/src/dashboard/DashboardReducer.ts
+++ b/src/dashboard/DashboardReducer.ts
@@ -19,7 +19,13 @@ import {
} from './DashboardActions';
export const NEODASH_VERSION = '2.4';
-export const VERSION_TO_MIGRATE = { '1.1': '2.0', '2.0': '2.1', '2.1': '2.2', '2.2': '2.3', '2.3': '2.4' };
+export const VERSION_TO_MIGRATE = {
+ '1.1': '2.0',
+ '2.0': '2.1',
+ '2.1': '2.2',
+ '2.2': '2.3',
+ '2.3': '2.4',
+};
export const initialState = {
title: DEFAULT_DASHBOARD_TITLE,
diff --git a/src/dashboard/DashboardThunks.ts b/src/dashboard/DashboardThunks.ts
index aa563d444..3ef4e62bf 100644
--- a/src/dashboard/DashboardThunks.ts
+++ b/src/dashboard/DashboardThunks.ts
@@ -3,13 +3,12 @@ import { updateDashboardSetting } from '../settings/SettingsActions';
import { addPage, movePage, removePage, resetDashboardState, setDashboard, setDashboardUuid } from './DashboardActions';
import { QueryStatus, runCypherQuery } from '../report/ReportQueryRunner';
import { setDraft, setParametersToLoadAfterConnecting, setWelcomeScreenOpen } from '../application/ApplicationActions';
-import { updateGlobalParametersThunk, updateParametersToNeo4jTypeThunk } from '../settings/SettingsThunks';
+import { updateGlobalParametersThunk } from '../settings/SettingsThunks';
import { createUUID } from '../utils/uuid';
import { createLogThunk } from '../application/logging/LoggingThunk';
import { applicationGetConnectionUser, applicationIsStandalone } from '../application/ApplicationSelectors';
import { applicationGetLoggingSettings } from '../application/logging/LoggingSelectors';
import { NEODASH_VERSION, VERSION_TO_MIGRATE } from './DashboardReducer';
-import { Date as Neo4jDate } from 'neo4j-driver-core/lib/temporal-types.js';
export const removePageThunk = (number) => (dispatch: any, getState: any) => {
try {
@@ -80,6 +79,17 @@ export const loadDashboardThunk = (uuid, text) => (dispatch: any, getState: any)
dashboard = dashboard.dashboard;
}
+ let patched;
+ [dashboard, patched] = patchDashboardVersion(dashboard, dashboard.version);
+ if (patched) {
+ dispatch(
+ createNotificationThunk(
+ 'Successfully patched dashboard',
+ `Your old dashboard has been patched. You might need to refresh this page and reactivate extensions.`
+ )
+ );
+ }
+
// Attempt upgrade if dashboard version is outdated.
while (VERSION_TO_MIGRATE[dashboard.version]) {
const upgradedDashboard = upgradeDashboardVersion(
@@ -102,16 +112,6 @@ export const loadDashboardThunk = (uuid, text) => (dispatch: any, getState: any)
throw `Invalid dashboard version: ${dashboard.version}. Try restarting the application, or retrieve your cached dashboard using a debug report.`;
}
- // Cast dashboard parameters from serialized format to correct types
- Object.keys(dashboard.settings.parameters).forEach((key) => {
- const value = dashboard.settings.parameters[key];
-
- // Serialized Date to Neo4jDate
- if (value && value.year && value.month && value.day) {
- dashboard.settings.parameters[key] = new Neo4jDate(value.year, value.month, value.day);
- }
- });
-
// Reverse engineer the minimal set of fields from the selection loaded.
dashboard.pages.forEach((p) => {
p.reports.forEach((r) => {
@@ -129,9 +129,8 @@ export const loadDashboardThunk = (uuid, text) => (dispatch: any, getState: any)
const { application } = getState();
dispatch(updateGlobalParametersThunk(application.parametersToLoadAfterConnecting));
+ dispatch(updateGlobalParametersThunk(dashboard.settings.parameters));
dispatch(setParametersToLoadAfterConnecting(null));
- dispatch(updateParametersToNeo4jTypeThunk());
-
// Pre-2.3.4 dashboards might now always have a UUID. Set it if not present.
if (!dashboard.uuid) {
dispatch(setDashboardUuid(uuid));
@@ -540,6 +539,29 @@ export const assignDashboardUuidIfNotPresentThunk = () => (dispatch: any, getSta
dispatch(setDashboardUuid(createUUID()));
}
};
+export function patchDashboardVersion(dashboard: any, version: any) {
+ let patched = false;
+ if (version == '2.4') {
+ dashboard.pages.forEach((p) => {
+ p.reports.forEach((r) => {
+ if (r.type == 'graph' || r.type == 'map' || r.type == 'graph3d') {
+ r.settings?.actionsRules?.forEach((rule) => {
+ if (
+ rule?.field &&
+ (rule?.condition === 'onNodeClick' || rule?.condition == 'Click') &&
+ rule.value.includes('.')
+ ) {
+ let val = rule.value.split('.');
+ rule.value = val[val.length - 1] || rule.value;
+ patched = true;
+ }
+ });
+ }
+ });
+ });
+ }
+ return [dashboard, patched];
+}
export function upgradeDashboardVersion(dashboard: any, origin: string, target: string) {
if (origin == '2.3' && target == '2.4') {
@@ -549,6 +571,19 @@ export function upgradeDashboardVersion(dashboard: any, origin: string, target:
r.y *= 2;
r.width *= 2;
r.height *= 2;
+
+ if (r.type == 'graph' || r.type == 'map' || r.type == 'graph3d') {
+ r.settings?.actionsRules?.forEach((rule) => {
+ if (
+ rule?.field &&
+ (rule?.condition === 'onNodeClick' || rule?.condition == 'Click') &&
+ rule.value.includes('.')
+ ) {
+ let val = rule.value.split('.');
+ rule.value = val[val.length - 1] || rule.value;
+ }
+ });
+ }
});
});
dashboard.version = '2.4';
diff --git a/src/dashboard/header/DashboardTitle.tsx b/src/dashboard/header/DashboardTitle.tsx
index f6c67d242..476321135 100644
--- a/src/dashboard/header/DashboardTitle.tsx
+++ b/src/dashboard/header/DashboardTitle.tsx
@@ -149,10 +149,10 @@ export const NeoDashboardTitle = ({
{/* If the app is not running in standalone mode (i.e. in edit mode) always show dashboard settings. */}
{!standaloneSettings.standalone ? (
+ {editable ? renderExtensionsButtons() : <>>}
{editable ? : <>>}
{editable ? : <>>}
- {editable ? renderExtensionsButtons() : <>>}
) : (
<>>
diff --git a/src/dashboard/sidebar/DashboardSidebar.tsx b/src/dashboard/sidebar/DashboardSidebar.tsx
index bfe580f10..50e6f25e4 100644
--- a/src/dashboard/sidebar/DashboardSidebar.tsx
+++ b/src/dashboard/sidebar/DashboardSidebar.tsx
@@ -4,7 +4,13 @@ import { getDashboardIsEditable, getPageNumber } from '../../settings/SettingsSe
import { getDashboardSettings, getDashboardTitle } from '../DashboardSelectors';
import { Button, SideNavigation, SideNavigationGroupHeader, SideNavigationList, TextInput } from '@neo4j-ndl/react';
import { removeReportThunk } from '../../page/PageThunks';
-import { PlusIconOutline, MagnifyingGlassIconOutline, CircleStackIconOutline } from '@neo4j-ndl/react/icons';
+import {
+ PlusIconOutline,
+ MagnifyingGlassIconOutline,
+ CircleStackIconOutline,
+ ArrowPathIconOutline,
+} from '@neo4j-ndl/react/icons';
+
import Tooltip from '@mui/material/Tooltip';
import { DashboardSidebarListItem } from './DashboardSidebarListItem';
import {
@@ -38,6 +44,7 @@ import NeoDashboardSidebarExportModal from './modal/DashboardSidebarExportModal'
import NeoDashboardSidebarDeleteModal from './modal/DashboardSidebarDeleteModal';
import NeoDashboardSidebarInfoModal from './modal/DashboardSidebarInfoModal';
import NeoDashboardSidebarShareModal from './modal/DashboardSidebarShareModal';
+import NeoDashboardSidebarAccessModal from './modal/DashboardSidebarAccessModal';
import LegacyShareModal from './modal/legacy/LegacyShareModal';
import { NEODASH_VERSION } from '../DashboardReducer';
@@ -61,6 +68,7 @@ enum Modal {
LOAD = 7,
SAVE = 8,
NONE = 9,
+ ACCESS = 10,
}
// We use "index = -1" to represent a non-saved draft dashboard in the sidebar's dashboard list.
@@ -250,6 +258,16 @@ export const NeoDashboardSidebar = ({
}}
/>
+ {
+ setModalOpen(Modal.NONE);
+ setCachedDashboard('');
+ }}
+ />
+
{
+ setMenuOpen(Menu.NONE);
+ setModalOpen(Modal.ACCESS);
+ }}
handleDeleteClicked={() => {
setMenuOpen(Menu.NONE);
setModalOpen(Modal.DELETE);
@@ -367,6 +389,33 @@ export const NeoDashboardSidebar = ({
Dashboards
+
+ {
+ getDashboardListFromNeo4j();
+ // When reloading, if the dashboard is not in DRAFT mode, we can directly refresh it.
+ if (!draft) {
+ const d = dashboards[selectedDashboardIndex];
+ loadDashboardFromNeo4j(driver, dashboardDatabase, d.uuid, (file) => {
+ loadDashboard(d.uuid, file);
+ });
+ }
+ }}
+ >
+
+
+
{/* Only let users create dashboards and change database when running in editor mode. */}
{!readonly || (readonly && standaloneSettings.standaloneLoadFromOtherDatabases) ? (
<>
@@ -379,7 +428,7 @@ export const NeoDashboardSidebar = ({
style={{
float: 'right',
marginLeft: '0px',
- marginRight: '12px',
+ marginRight: '3px',
paddingLeft: 0,
paddingRight: '3px',
}}
diff --git a/src/dashboard/sidebar/menu/DashboardSidebarDashboardMenu.tsx b/src/dashboard/sidebar/menu/DashboardSidebarDashboardMenu.tsx
index 67159dcc2..eea79efb4 100644
--- a/src/dashboard/sidebar/menu/DashboardSidebarDashboardMenu.tsx
+++ b/src/dashboard/sidebar/menu/DashboardSidebarDashboardMenu.tsx
@@ -8,6 +8,7 @@ import {
DocumentTextIconOutline,
InformationCircleIconOutline,
ShareIconOutline,
+ FingerPrintIconOutline,
TrashIconOutline,
XMarkIconOutline,
} from '@neo4j-ndl/react/icons';
@@ -25,6 +26,7 @@ export const NeoDashboardSidebarDashboardMenu = ({
handleLoadClicked,
handleExportClicked,
handleShareClicked,
+ handleAccessClicked,
handleDeleteClicked,
handleClose,
}) => {
@@ -49,6 +51,7 @@ export const NeoDashboardSidebarDashboardMenu = ({
} title='Load' />
{/* {}} icon={ } title='Clone' /> */}
} title='Export' />
+ } title='Access' />
} title='Share' />
} title='Delete' />
diff --git a/src/dashboard/sidebar/modal/DashboardSidebarAccessModal.tsx b/src/dashboard/sidebar/modal/DashboardSidebarAccessModal.tsx
new file mode 100644
index 000000000..0cb320b65
--- /dev/null
+++ b/src/dashboard/sidebar/modal/DashboardSidebarAccessModal.tsx
@@ -0,0 +1,215 @@
+import React, { useEffect, useState, useContext } from 'react';
+import { IconButton, Button, Dialog, TextInput } from '@neo4j-ndl/react';
+import { Menu, MenuItem, Chip } from '@mui/material';
+import { Neo4jContext, Neo4jContextState } from 'use-neo4j/dist/neo4j.context';
+import { PlusCircleIconOutline } from '@neo4j-ndl/react/icons';
+import { QueryStatus, runCypherQuery } from '../../../report/ReportQueryRunner';
+import { createNotificationThunk } from '../../../page/PageThunks';
+import { useDispatch } from 'react-redux';
+/**
+ * Configures setting the current Neo4j database connection for the dashboard.
+ * @param open - Whether the modal is open or not.
+ * @param database - The current Neo4j database.
+ * @param dashboard - The current dashboard.
+ * @param handleClose - The function to close the modal.
+ */
+export const NeoDashboardSidebarAccessModal = ({ open, database, dashboard, handleClose }) => {
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [selectedLabels, setSelectedLabels] = useState([]);
+ const [allLabels, setAllLabels] = useState([]);
+ const [neo4jLabels, setNeo4jLabels] = useState([]);
+ const [newLabel, setNewLabel] = useState('');
+ const INITIAL_LABEL = '_Neodash_Dashboard';
+ const [feedback, setFeedback] = useState('');
+ const { driver } = useContext(Neo4jContext);
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ if (!open) {
+ return;
+ }
+ runCypherQuery(
+ driver,
+ database,
+ 'CALL db.labels()',
+ {},
+ 1000,
+ () => {},
+ (records) => setNeo4jLabels(records.map((record) => record.get('label')))
+ );
+
+ const query = `
+ MATCH (d:${INITIAL_LABEL} {uuid: "${dashboard.uuid}"})
+ RETURN labels(d) as labels
+ `;
+ runCypherQuery(
+ driver,
+ database,
+ query,
+ {},
+ 1000,
+ (error) => {
+ console.error(error);
+ },
+ (records) => {
+ // Set the selectedLabels state to the labels of the dashboard
+ setSelectedLabels(records[0].get('labels'));
+ setAllLabels(records[0].get('labels'));
+ }
+ );
+ setFeedback('');
+ setNewLabel('');
+ }, [open]);
+
+ useEffect(() => {
+ setAllLabels([INITIAL_LABEL]);
+ setSelectedLabels([INITIAL_LABEL]);
+ }, []);
+
+ const handleOpenMenu = (event) => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleCloseMenu = () => {
+ setAnchorEl(null);
+ };
+
+ const handleLabelSelect = (label) => {
+ if (!selectedLabels.includes(label) && label !== INITIAL_LABEL) {
+ setSelectedLabels([...selectedLabels, label]);
+ }
+ handleCloseMenu();
+ };
+
+ const handleDeleteLabel = (label) => {
+ if (label !== INITIAL_LABEL) {
+ const updatedLabels = selectedLabels.filter((selectedLabel) => selectedLabel !== label);
+ setSelectedLabels(updatedLabels);
+ }
+ };
+
+ const handleAddNewLabel = (e) => {
+ if (e.key === 'Enter' && newLabel.trim() !== '') {
+ if (selectedLabels.includes(newLabel)) {
+ setFeedback('Label already exists. Please enter a unique label.');
+ handleCloseMenu();
+ } else {
+ setSelectedLabels([...selectedLabels, newLabel]);
+ handleLabelSelect(newLabel);
+ setNewLabel('');
+ handleCloseMenu();
+ setFeedback('');
+ }
+ }
+ };
+
+ const handleSave = () => {
+ // Finding the difference between what is stored and what has been selected in the UI
+ let toDelete = allLabels.filter((item) => selectedLabels.indexOf(item) < 0);
+
+ const query = `
+ MATCH (d:${INITIAL_LABEL} {uuid: "${dashboard.uuid}"})
+ SET d:${selectedLabels.join(':')}
+ ${toDelete.length > 0 ? `REMOVE d:${toDelete.join(':')}` : ''}
+ RETURN 1;
+ `;
+
+ runCypherQuery(
+ driver,
+ database,
+ query,
+ { selectedLabels: selectedLabels },
+ 1000,
+ (status) => {
+ if (status == QueryStatus.COMPLETE) {
+ dispatch(
+ createNotificationThunk(
+ '🎉 Success!',
+ 'Selected Labels have successfully been added to the dashboard node.'
+ )
+ );
+ handleClose();
+ } else {
+ dispatch(
+ createNotificationThunk(
+ 'Unable to save dashboard',
+ `Do you have write access to the '${database}' database?`
+ )
+ );
+ }
+ },
+ () => {}
+ );
+ };
+
+ return (
+
+ Dasboard Access Control - '{dashboard?.title}'
+
+ Welcome to the Dashboard Access settings!
+
+ In this modal, you can select the labels that you want to add to the current dashboard node.
+
+ For more information, please refer to the{' '}
+
+ documentation
+
+ .
+
+
+
+ {/* Fetch labels dynamically from Neo4j and map to menu items */}
+ {neo4jLabels
+ .filter((e) => !selectedLabels.includes(e))
+ .map((label) => (
+ handleLabelSelect(label)}>
+ {label}
+
+ ))}
+
+ setNewLabel(e.target.value)}
+ onKeyDown={(e: KeyboardEvent) => {
+ handleAddNewLabel(e);
+ e.stopPropagation();
+ }}
+ errorText={feedback}
+ placeholder='Create New label'
+ autoComplete='off'
+ />
+
+
+
+ {selectedLabels.map((label) => (
+
handleDeleteLabel(label)}
+ style={{ marginRight: '5px', marginBottom: '5px' }}
+ />
+ ))}
+
+
+
+
+
+
+
+ Cancel
+
+
+ Save
+
+
+
+ );
+};
+
+export default NeoDashboardSidebarAccessModal;
diff --git a/src/extensions/ExtensionConfig.tsx b/src/extensions/ExtensionConfig.tsx
index a94053d2e..2ddfc0620 100644
--- a/src/extensions/ExtensionConfig.tsx
+++ b/src/extensions/ExtensionConfig.tsx
@@ -5,6 +5,7 @@ import NeoOverrideCardQueryEditor from './text2cypher/component/OverrideCardQuer
import { translateQuery } from './text2cypher/util/Util';
import { GPT_LOADING_ICON } from './text2cypher/component/LoadingIcon';
import QueryTranslatorButton from './text2cypher/component/QueryTranslatorButton';
+import RBACManagementLabelButton from './rbac/RBACManagementLabelButton';
// TODO: continue documenting interface
interface Extension {
@@ -82,6 +83,17 @@ export const EXTENSIONS: Record = {
'Forms let you craft Cypher queries with multiple inputs, that are fired on demand. Using parameters from the dashboard, or form specific input, you will be able to trigger custom logic with forms.',
link: 'https://neo4j.com/professional-services/',
},
+ 'access-control-management': {
+ name: 'access-control-management',
+ label: 'Access Control Management',
+ author: 'Neo4j Professional Services',
+ image: 'accesscontrol2.jpg',
+ enabled: true,
+ description:
+ 'This extension lets you manage access control, letting you assign users to roles, as well as controlling which node labels can be read by a user.',
+ link: 'https://neo4j.com/professional-services/',
+ settingsMenuButton: RBACManagementLabelButton,
+ },
};
/**
diff --git a/src/extensions/actions/ActionsRuleCreationModal.tsx b/src/extensions/actions/ActionsRuleCreationModal.tsx
index 38ebec995..48d6486ad 100644
--- a/src/extensions/actions/ActionsRuleCreationModal.tsx
+++ b/src/extensions/actions/ActionsRuleCreationModal.tsx
@@ -236,9 +236,9 @@ export const NeoCustomReportActionsModal = ({
return [];
};
- const createFieldVariableSuggestionsFromRule = (rule, type) => {
- let suggestions;
- if (type) {
+ const createFieldVariableSuggestionsFromRule = (rule, skipRuleFieldCheck) => {
+ let suggestions: string[];
+ if (skipRuleFieldCheck) {
suggestions = createFieldVariableSuggestions(rule.condition, true, null).filter((e) =>
e.toLowerCase().startsWith(rule.field.toLowerCase())
);
@@ -249,13 +249,11 @@ export const NeoCustomReportActionsModal = ({
e.toLowerCase().startsWith(rule.value.toLowerCase())
);
}
-
// When we are accessing node properties (not page names), parse the node label + property pair to only show properties.
// Fields for graph and map reports are structured differently than regular reports (table, bar, etc.), so we access suggestions differently.
if (rule.customization !== 'set page' && (type == 'graph' || type == 'map' || type == 'graph3d')) {
suggestions = suggestions.map((e) => e.split('.')[1] || e);
}
-
return suggestions;
};
diff --git a/src/extensions/advancedcharts/Utils.ts b/src/extensions/advancedcharts/Utils.ts
index aa7c1f840..647223918 100644
--- a/src/extensions/advancedcharts/Utils.ts
+++ b/src/extensions/advancedcharts/Utils.ts
@@ -1,7 +1,6 @@
import { valueIsArray } from '../../chart/ChartUtils';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
import { getPageNumbersAndNames } from '../../dashboard/DashboardSelectors';
-import { updateDashboardSetting } from '../../settings/SettingsActions';
export const getRule = (e, rules, type) => {
let r = getRuleWithFieldPropertyName(e, rules, type, null);
diff --git a/src/extensions/advancedcharts/chart/gantt/Utils.ts b/src/extensions/advancedcharts/chart/gantt/Utils.ts
index 012c77201..331be20ff 100644
--- a/src/extensions/advancedcharts/chart/gantt/Utils.ts
+++ b/src/extensions/advancedcharts/chart/gantt/Utils.ts
@@ -1,3 +1,4 @@
+import { toNumber } from '../../../../chart/ChartUtils';
import { buildGraphVisualizationObjectFromRecords } from '../../../../chart/graph/util/RecordUtils';
import date_utils from './frappe/lib/date_utils';
@@ -97,9 +98,9 @@ export function createTasksList(
return undefined;
}
}
- return {
- start: new Date(neoStartDate.year, neoStartDate.month, neoStartDate.day),
- end: new Date(neoEndDate.year, neoEndDate.month, neoEndDate.day),
+ let res = {
+ start: new Date(toNumber(neoStartDate.year), toNumber(neoStartDate.month), toNumber(neoStartDate.day)),
+ end: new Date(toNumber(neoEndDate.year), toNumber(neoEndDate.month), toNumber(neoEndDate.day)),
name: name || '(undefined)',
labels: n.labels,
dependencies: dependencies[n.id],
@@ -112,6 +113,7 @@ export function createTasksList(
isDisabled: true,
styles: { progressColor: '#ffbb54', progressSelectedColor: '#ff9e0d' },
};
+ return res;
})
.filter((i) => i !== undefined);
}
diff --git a/src/extensions/forms/FormsReportConfig.tsx b/src/extensions/forms/FormsReportConfig.tsx
index 4367a7497..5918a4425 100644
--- a/src/extensions/forms/FormsReportConfig.tsx
+++ b/src/extensions/forms/FormsReportConfig.tsx
@@ -46,7 +46,7 @@ export const FORMS = {
label: 'Clear parameters after submit',
type: SELECTION_TYPES.LIST,
values: [true, false],
- default: false,
+ default: true,
},
hasResetButton: {
label: 'Has Reset Button',
diff --git a/src/extensions/forms/chart/NeoForm.tsx b/src/extensions/forms/chart/NeoForm.tsx
index e8d33641e..b1608c644 100644
--- a/src/extensions/forms/chart/NeoForm.tsx
+++ b/src/extensions/forms/chart/NeoForm.tsx
@@ -7,6 +7,7 @@ import { REPORT_LOADING_ICON } from '../../../report/Report';
import debounce from 'lodash/debounce';
import { RUN_QUERY_DELAY_MS } from '../../../config/ReportConfig';
import NeoParameterSelectionChart from '../../../chart/parameter/ParameterSelectionChart';
+import { checkParametersNameInGlobalParameter, extractAllParameterNames } from '../../../utils/parameterUtils';
enum FormStatus {
DATA_ENTRY = 0, // The user is filling in the form.
@@ -26,7 +27,7 @@ const NeoForm = (props: ChartProps) => {
const hasResetButton = settings?.hasResetButton ?? true;
const hasSubmitButton = settings?.hasSubmitButton ?? true;
const hasSubmitMessage = settings?.hasSubmitMessage ?? true;
- const clearParametersAfterSubmit = settings?.clearParametersAfterSubmit ?? false;
+ const clearParametersAfterSubmit = settings?.clearParametersAfterSubmit ?? true;
const [submitButtonActive, setSubmitButtonActive] = React.useState(true);
const [status, setStatus] = React.useState(FormStatus.DATA_ENTRY);
const [formResults, setFormResults] = React.useState([]);
@@ -42,6 +43,14 @@ const NeoForm = (props: ChartProps) => {
});
}
+ const isParametersDefined = (cypherQuery: string | undefined) => {
+ const parameterNames = extractAllParameterNames(cypherQuery);
+ if (props.parameters) {
+ return checkParametersNameInGlobalParameter(parameterNames, props.parameters);
+ }
+ return false;
+ };
+
useEffect(() => {
// If the parameters change after the form is completed, reset it, as there might be another submission.
if (status == FormStatus.SUBMITTED) {
@@ -77,7 +86,7 @@ const NeoForm = (props: ChartProps) => {
{
if (!props.query || !props.query.trim()) {
props.createNotification(
diff --git a/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx b/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx
index fc9fec0ad..87515c24b 100644
--- a/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx
+++ b/src/extensions/forms/settings/NeoFormCardSettingsModal.tsx
@@ -4,6 +4,7 @@ import React from 'react';
import { Button, Dialog } from '@neo4j-ndl/react';
import ParameterSelectCardSettings from '../../../chart/parameter/ParameterSelectCardSettings';
import NeoCardSettingsFooter from '../../../card/settings/CardSettingsFooter';
+import { objMerge } from '../../../utils/ObjectManipulation';
const NeoFormCardSettingsModal = ({ open, setOpen, index, formFields, setFormFields, database, extensions }) => {
const [advancedSettingsOpen, setAdvancedSettingsOpen] = React.useState(false);
@@ -24,15 +25,19 @@ const NeoFormCardSettingsModal = ({ open, setOpen, index, formFields, setFormFie
query={formFields[index].query}
type={'select'}
database={database}
- settings={formFields[index].settings}
+ settings={objMerge({ inputMode: 'cypher' }, formFields[index].settings)}
extensions={extensions}
onReportSettingUpdate={(key, value) => {
const newFormFields = [...formFields];
newFormFields[index].settings[key] = value;
+ if (key == 'type') {
+ newFormFields[index].type = value;
+ }
setFormFields(newFormFields);
}}
onQueryUpdate={(query) => {
const newFormFields = [...formFields];
+
newFormFields[index].query = query;
setFormFields(newFormFields);
}}
diff --git a/src/extensions/rbac/RBACManagementLabelButton.tsx b/src/extensions/rbac/RBACManagementLabelButton.tsx
new file mode 100644
index 000000000..80bf9e19e
--- /dev/null
+++ b/src/extensions/rbac/RBACManagementLabelButton.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { IconButton, MenuItem } from '@neo4j-ndl/react';
+import { UserCircleIconOutline } from '@neo4j-ndl/react/icons';
+import { RBACManagementMenu } from './RBACManagementMenu';
+
+import Tooltip from '@mui/material/Tooltip/Tooltip';
+import { createNotificationThunk } from '../../page/PageThunks';
+
+const RBACManagementLabelButton = ({ createNotification }) => {
+ const [MenuOpen, setMenuOpen] = React.useState(false);
+ const [anchorEl, setAnchorEl] = React.useState(null);
+
+ const handleButtonClick = (event) => {
+ setMenuOpen(true);
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleClose = () => {
+ setMenuOpen(false);
+ };
+
+ const button = (
+
+
+
+
+
+ );
+
+ return (
+
+ {button}
+
+
+ );
+};
+
+const mapStateToProps = () => ({});
+
+const mapDispatchToProps = (dispatch) => ({
+ createNotification: (title: any, message: any) => {
+ dispatch(createNotificationThunk(title, message));
+ },
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(RBACManagementLabelButton);
diff --git a/src/extensions/rbac/RBACManagementMenu.tsx b/src/extensions/rbac/RBACManagementMenu.tsx
new file mode 100644
index 000000000..31ec37f81
--- /dev/null
+++ b/src/extensions/rbac/RBACManagementMenu.tsx
@@ -0,0 +1,86 @@
+import React, { useEffect, useState, useContext } from 'react';
+import { Menu, MenuItem, MenuItems } from '@neo4j-ndl/react';
+import { UserIconOutline } from '@neo4j-ndl/react/icons';
+import { Neo4jContext, Neo4jContextState } from 'use-neo4j/dist/neo4j.context';
+import { QueryStatus, runCypherQuery } from '../../report/ReportQueryRunner';
+import RBACManagementModal from './RBACManagementModal';
+
+/**
+ * Component for providing a menu of all the roles in the neo4j database to the user whenever they press on the
+ * RBACManagementLabelButton.
+ */
+export const RBACManagementMenu = ({ anchorEl, MenuOpen, handleClose, createNotification }) => {
+ const { driver } = useContext(Neo4jContext);
+ const [roles, setRoles] = useState([]);
+ const [selectedRole, setSelectedRole] = useState(null);
+ const [isModalOpen, setIsModalOpen] = useState(false);
+
+ useEffect(() => {
+ if (!MenuOpen) {
+ return;
+ }
+ const query = `SHOW PRIVILEGES YIELD role, action WHERE role <> "PUBLIC" RETURN role, 'dbms_actions' in collect(action)`;
+ runCypherQuery(
+ driver,
+ 'system',
+ query,
+ {},
+ 1000,
+ () => {},
+ (records) => {
+ if (records[0].error) {
+ createNotification('Unable to retrieve roles', records[0].error);
+ return;
+ }
+ // Only display roles which are not able to do 'dbms_actions', i.e. they are not admins.
+ setRoles(records.filter((r) => r._fields[1] == false).map((record) => record._fields[0]));
+ }
+ );
+ }, [MenuOpen]);
+
+ if (roles.length == 0) {
+ return <>>;
+ }
+
+ const handleRoleClicked = (role) => {
+ handleClose();
+ setSelectedRole(role);
+ setIsModalOpen(true);
+ };
+
+ return (
+ <>
+
+
+ {roles.map((role) => (
+ handleRoleClicked(role)} icon={ } title={role} />
+ ))}
+
+
+
+ {
+ setIsModalOpen(false);
+ }}
+ currentRole={selectedRole}
+ createNotification={createNotification}
+ />
+ >
+ );
+};
+
+export default RBACManagementMenu;
diff --git a/src/extensions/rbac/RBACManagementModal.tsx b/src/extensions/rbac/RBACManagementModal.tsx
new file mode 100644
index 000000000..a69e211bc
--- /dev/null
+++ b/src/extensions/rbac/RBACManagementModal.tsx
@@ -0,0 +1,276 @@
+import React, { useEffect, useState, useContext } from 'react';
+import { Button, Dialog, Dropdown } from '@neo4j-ndl/react';
+import { Neo4jContext, Neo4jContextState } from 'use-neo4j/dist/neo4j.context';
+import {
+ Operation,
+ retrieveAllowAndDenyLists,
+ retrieveDatabaseList,
+ retrieveLabelsList,
+ retrieveNeo4jUsers,
+ updatePrivileges,
+ updateUsers,
+} from './RBACUtils';
+/**
+ * Configures RBAC Access Control Management for a certain role on certain labels and attaches the roles to specific users.
+ * @param open - Whether the modal is open or not.
+ * @param currentRole - The currently selected role.
+ * @param handleClose - The function to close the modal.
+ */
+export const RBACManagementModal = ({ open, handleClose, currentRole, createNotification }) => {
+ const { driver } = useContext(Neo4jContext);
+ const [neo4jUsers, setNeo4jUsers] = useState([]);
+ const [selectedUsers, setSelectedUsers] = useState([]);
+ const [selectedDatabase, setSelectedDatabase] = useState('');
+ const [databases, setDatabases] = useState([]);
+ const [loaded, setLoaded] = useState(false);
+ const [labels, setLabels] = useState([]);
+ const [allowList, setAllowList] = useState([]);
+ const [denyList, setDenyList] = useState([]);
+ const [fixedAllowList, setFixedAllowList] = useState([]);
+ const [fixedDenyList, setFixedDenyList] = useState([]);
+ const [denyCompleted, setDenyCompleted] = useState(false);
+ const [allowCompleted, setAllowCompleted] = useState(false);
+ const [usersCompleted, setUsersCompleted] = useState(false);
+ const [failed, setFailed] = useState(false);
+ const [isDatabaseEmpty, setIsDatabaseEmpty] = useState(false);
+
+ useEffect(() => {
+ if (!open) {
+ setSelectedUsers([]);
+ setAllowList([]);
+ setDenyList([]);
+ setSelectedDatabase('');
+ return;
+ }
+ setDenyCompleted(false);
+ setAllowCompleted(false);
+ setUsersCompleted(false);
+ setFailed(false);
+ retrieveDatabaseList(driver, setDatabases);
+ retrieveNeo4jUsers(driver, currentRole, setNeo4jUsers, setSelectedUsers);
+ }, [open]);
+
+ useEffect(() => {
+ if (failed !== false) {
+ createNotification('Unable to update privileges', `${failed}`);
+ } else if (denyCompleted && allowCompleted && usersCompleted) {
+ createNotification('Success', `Access for role '${currentRole}' updated.`);
+ }
+ }, [denyCompleted, allowCompleted, usersCompleted, failed]);
+
+ const parseLabelsList = (database, records) => {
+ const allLabels = records.map((record) => record._fields[0]).filter((l) => l !== '_Neodash_Dashboard');
+ retrieveAllowAndDenyLists(
+ driver,
+ database,
+ currentRole,
+ allLabels,
+ setLabels,
+ setAllowList,
+ setDenyList,
+ setFixedAllowList,
+ setFixedDenyList,
+ setLoaded
+ );
+ };
+
+ const handleDatabaseSelect = (selectedOption) => {
+ setSelectedDatabase(selectedOption.value);
+ setLabels([]);
+ setAllowList([]);
+ setDenyList([]);
+ retrieveLabelsList(driver, selectedOption.value, (records) => {
+ if (records.length === 0) {
+ setIsDatabaseEmpty(true);
+ } else {
+ parseLabelsList(selectedOption.value, records);
+ setIsDatabaseEmpty(false);
+ }
+ });
+ };
+
+ const handleSave = async () => {
+ createNotification('Updating', `Access for role '${currentRole}' is being updated, please wait...`);
+ try {
+ await updateUsers(
+ driver,
+ currentRole,
+ neo4jUsers,
+ selectedUsers,
+ () => setUsersCompleted(true),
+ (failReason) => setFailed(`Operation 'ROLE-USER ASSIGNMENT' failed.\n Reason: ${failReason}`)
+ );
+
+ if (selectedDatabase && labels.length > 0) {
+ // Check if there are labels to update
+ const nonFixedDenyList = denyList.filter((n) => !fixedDenyList.includes(n));
+ const nonFixedAllowList = allowList.filter((n) => !fixedDenyList.includes(n));
+
+ await updatePrivileges(
+ driver,
+ selectedDatabase,
+ currentRole,
+ labels,
+ nonFixedDenyList,
+ Operation.DENY,
+ () => setDenyCompleted(true),
+ (failReason) => setFailed(`Operation 'DENY LABEL ACCESS' failed.\n Reason: ${failReason}`)
+ );
+
+ await updatePrivileges(
+ driver,
+ selectedDatabase,
+ currentRole,
+ labels,
+ nonFixedAllowList,
+ Operation.GRANT,
+ () => setAllowCompleted(true),
+ (failReason) => setFailed(`Operation 'ALLOW LABEL ACCESS' failed.\n Reason: ${failReason}`)
+ );
+ } else {
+ // Since there is no database or labels selected, we don't run the DENY/ALLOW queries.
+ // We just mark them as completed so the success message shows up.
+ setDenyCompleted(true);
+ setAllowCompleted(true);
+ }
+ } catch (error) {
+ // Handle any errors that occur during the update process
+ createNotification('error', `An error occurred: ${error.message}`);
+ } finally {
+ handleClose();
+ }
+ };
+
+ return (
+
+ Access Control - '{currentRole}'
+
+ This screen lets you handle user assignment and access control for a specific role.
+
+ For more information, please refer to the{' '}
+
+ documentation
+
+ .
+
+
+
+
Manage Users
+
Select a list of users to assign to the current role.
+
({ value: user, label: user })),
+ options: neo4jUsers.map((user) => ({ value: user, label: user })),
+ isMulti: true,
+ onChange: (val) => setSelectedUsers(val.map((v) => v.value)),
+ }}
+ />
+
+
+
+
Label Access
+
For a given database, control what labels the role is or is not allowed to see.
+
database !== 'system')
+ .map((database) => ({ value: database, label: database })),
+ onChange: handleDatabaseSelect,
+ }}
+ />
+ {selectedDatabase && isDatabaseEmpty && (
+
+ This database is currently empty. Please select a different database or add labels to manage access.
+
+ )}
+
+ {selectedDatabase && !isDatabaseEmpty && loaded && (
+ <>
+
+
+
+ i == '*') &&
+ 'Selecting (*) grants access to all labels, overriding other selections.'
+ }
+ selectProps={{
+ placeholder: 'Select labels',
+ isClearable: false,
+ value: allowList.map((nodelabel) => ({ value: nodelabel, label: nodelabel })),
+ options: labels.map((nodelabel) => ({ value: nodelabel, label: nodelabel })),
+ isMulti: true,
+ onChange: (val) => {
+ // Make sure that only database-specific label access rules can be changed from this UI.
+ if (fixedAllowList.every((v) => val.map((selected) => selected.value).includes(v))) {
+ setAllowList(val.map((v) => v.value));
+ } else {
+ createNotification(
+ 'Label cannot be removed',
+ 'The selected label is allowed access across all databases. You cannot remove this privilege using this interface.'
+ );
+ }
+ },
+ }}
+ />
+
+
+ i == '*') &&
+ 'Selecting (*) denies access to all labels, overriding other selections.'
+ }
+ selectProps={{
+ placeholder: 'Select labels',
+ isClearable: false,
+ value: denyList.map((nodelabel) => ({ value: nodelabel, label: nodelabel })),
+ options: labels
+ .filter((l) => l !== '*')
+ .map((nodelabel) => ({ value: nodelabel, label: nodelabel })),
+ isMulti: true,
+ onChange: (val) => {
+ // Make sure that only database-specific label access rules can be changed from this UI.
+ if (fixedDenyList.every((v) => val.map((selected) => selected.value).includes(v))) {
+ setDenyList(val.map((v) => v.value));
+ } else {
+ createNotification(
+ 'Label cannot be removed',
+ 'The selected label is denied access across all databases. You cannot remove this privilege using this interface.'
+ );
+ }
+ },
+ }}
+ />
+
+
+ >
+ )}
+
+
+
+ Cancel
+
+
+ Save
+
+
+
+ );
+};
+
+export default RBACManagementModal;
diff --git a/src/extensions/rbac/RBACUtils.ts b/src/extensions/rbac/RBACUtils.ts
new file mode 100644
index 000000000..6fc56d375
--- /dev/null
+++ b/src/extensions/rbac/RBACUtils.ts
@@ -0,0 +1,309 @@
+import { QueryStatus, runCypherQuery } from '../../report/ReportQueryRunner';
+
+export enum Operation {
+ GRANT,
+ DENY,
+}
+
+/**
+ * Sets the privileges for a role to a new list provided by the user.
+ * This involves wiping old privileges, including a special case for '*' privileges.
+ * @param driver the Neo4j driver.
+ * @param database a database name for which Privileges must be changed.
+ * @param role role for which privileges are updated.
+ * @param allLabels list of all labels in the given database.
+ * @param newLabels list of new labels in the database, for which priveleges are changed.
+ * @param operation The operation, either 'GRANT' or 'DENY'
+ */
+export async function updatePrivileges(
+ driver,
+ database,
+ role,
+ allLabels,
+ newLabels,
+ operation: Operation,
+ onSuccess,
+ onFail
+) {
+ // TODO - should we also drop cross-database DENYs (`ON GRAPH *`) to catch the true full set?
+ // TODO - there
+ // 1. Special case for '*'. Create it if needed to be there, otherwise revoke it.
+ runCypherQuery(
+ driver,
+ 'system',
+ buildAccessQuery(database, role, ['*'], operation, !newLabels.includes('*')),
+ {},
+ 1000,
+ (status) => {
+ if (status == QueryStatus.NO_DATA || QueryStatus.COMPLETE) {
+ // 2. Build the query that revokes all possible priveleges, returning to a 'blank slate'
+ runCypherQuery(
+ driver,
+ 'system',
+ buildAccessQuery(
+ database,
+ role,
+ allLabels.filter((l) => l !== '*'),
+ operation,
+ true
+ ),
+ {},
+ 1000,
+ (status) => {
+ if (status == QueryStatus.NO_DATA || status == QueryStatus.COMPLETE) {
+ // TODO: Neo4j is very slow in updating after the previous query, even though it is technically a finished query.
+ // We build in an artificial delay...
+ const timeout = setTimeout(() => {
+ // 3. Create the new privileges as specified in the `newLabels` list by the user.
+ if (newLabels.filter((l) => l !== '*').length > 0) {
+ runCypherQuery(
+ driver,
+ 'system',
+ buildAccessQuery(
+ database,
+ role,
+ newLabels.filter((l) => l !== '*'),
+ operation,
+ false
+ ),
+ {},
+ 1000,
+ (status) => {
+ if (status == QueryStatus.NO_DATA || status == QueryStatus.COMPLETE) {
+ onSuccess();
+ }
+ },
+ (records) => {
+ if (records && records[0] && records[0].error) {
+ onFail(records[0].error);
+ }
+ }
+ );
+ } else {
+ onSuccess();
+ }
+ }, 1000);
+ }
+ },
+ (records) => {
+ if (records && records[0] && records[0].error) {
+ onFail(records[0].error);
+ }
+ }
+ );
+ }
+ },
+ (records) => {
+ if (records && records[0] && records[0].error) {
+ onFail(records[0].error);
+ }
+ }
+ );
+}
+
+/**
+ * Generic query builder for adding/removing grants/denies for a list of labels.
+ * @param database the database to grant/deny on.
+ * @param role the role to create access rules for.
+ * @param labels a list of node labels
+ * @param access the access type. Can be "GRANT" or "DENY"
+ * @param revoke Whether to revoke access or not.
+ * @returns
+ */
+function buildAccessQuery(database, role, labels, operation: Operation, revoke: boolean): string {
+ const query = `${revoke ? 'REVOKE' : ''}
+ ${operation == Operation.DENY ? 'DENY' : 'GRANT'}
+ MATCH {*} ON GRAPH ${database}
+ NODES ${labels.join(',')}
+ ${revoke ? 'FROM' : 'TO'} ${role}`;
+ return query;
+}
+
+/**
+ * Retrieve allow and deny lists for a selected role, and a given database.
+ * @param driver Neo4j driver object.
+ * @param database the user's selected database.
+ * @param currentRole the user's selected role.
+ * @param allLabels list of all labels in the database (retrieved seperately)
+ * @param setLabels callback to update the list of all labels with any more that may only exist in priveleges
+ * @param setAllowList callback to update the allow list retrieved from the database.
+ * @param setDenyList callback to update the deny list retrieved from the database.
+ * @param setLoaded callback to indicate the retrieval is completed.
+ */
+export const retrieveAllowAndDenyLists = (
+ driver,
+ database,
+ currentRole,
+ allLabels,
+ setLabels,
+ setAllowList,
+ setDenyList,
+ setFixedAllowList,
+ setFixedDenyList,
+ setLoaded
+) => {
+ runCypherQuery(
+ driver,
+ 'system',
+ `SHOW PRIVILEGES
+ YIELD graph, role, access, action, segment
+ WHERE (graph = $database OR graph = '*')
+ AND role = $rolename
+ AND action = 'match'
+ AND segment STARTS WITH 'NODE('
+ RETURN access, collect(substring(segment, 5, size(segment)-6)) as nodes, graph = "*" as fixed`,
+ { rolename: currentRole, database: database },
+ 1000,
+ (status) => {
+ if (status == QueryStatus.NO_DATA) {
+ setLabels(['*'].concat(allLabels));
+ setLoaded(true);
+ }
+ },
+ (records) => {
+ // Extract granted and denied label list from the result of the SHOW PRIVILEGES query
+ const grants = records.filter((r) => r._fields[0] == 'GRANTED' && r._fields[2] == false);
+ const denies = records.filter((r) => r._fields[0] == 'DENIED' && r._fields[2] == false);
+ const grantedLabels = grants[0] ? [...new Set(grants[0]._fields[1])] : [];
+ const deniedLabels = denies[0] ? [...new Set(denies[0]._fields[1])] : [];
+
+ // Do the same for fixed grants (those stored under the '*' graph permission)
+ const fixedGrants = records.filter((r) => r._fields[0] == 'GRANTED' && r._fields[2] == true);
+ const fixedDenies = records.filter((r) => r._fields[0] == 'DENIED' && r._fields[2] == true);
+ const fixedGrantedLabels = fixedGrants[0] ? [...new Set(fixedGrants[0]._fields[1])] : [];
+ const fixedDeniedLabels = fixedDenies[0] ? [...new Set(fixedDenies[0]._fields[1])] : [];
+
+ setAllowList([...new Set(grantedLabels.concat(fixedGrantedLabels))]);
+ setDenyList([...new Set(deniedLabels.concat(fixedDeniedLabels))]);
+ setFixedAllowList(fixedGrantedLabels);
+ setFixedDenyList(fixedDeniedLabels);
+
+ // Here we build a set of all POSSIBLE labels, that includes the list in the database, plus those in denies and grants.
+ const possibleLabels = [...new Set(allLabels.concat(grantedLabels).concat(deniedLabels))];
+ // Add '*' as an extra option.
+ setLabels(['*'].concat(possibleLabels));
+ setLoaded(true);
+ }
+ );
+};
+
+/**
+ * Retrieve the set of all users from the database.
+ * @param driver Neo4j driver object with active session.
+ * @param currentRole selected role.
+ * @param setNeo4jUsers callback to update the list of all users.
+ * @param setRoleUsers callback to update the list of role-specific users.
+ */
+export const retrieveNeo4jUsers = (driver, currentRole, setNeo4jUsers, setRoleUsers) => {
+ runCypherQuery(
+ driver,
+ 'system',
+ 'SHOW users yield user, roles return user, roles',
+ {},
+ 1000,
+ () => {},
+ (records) => {
+ const roleRecords = records.filter((r) => r._fields[1].includes(currentRole));
+ setRoleUsers(roleRecords.map((record) => record._fields[0]));
+ setNeo4jUsers(records.map((record) => record._fields[0]));
+ }
+ );
+};
+
+/**
+ * retrieve the list of labels in a given database from the dbms.
+ * @param driver Neo4j driver object.
+ * @param database selected database.
+ * @param setLabels callback to update the list of labels.
+ */
+export function retrieveLabelsList(driver, database: any, setLabels: (records: any) => void) {
+ let labelsSet = false; // Flag to track if setLabels was called
+
+ // Wrapper around the original setLabels to set the flag when called
+ const wrappedSetLabels = (records) => {
+ labelsSet = true;
+ setLabels(records);
+ };
+
+ runCypherQuery(driver, database, 'CALL db.labels()', {}, 1000, () => {}, wrappedSetLabels)
+ .then(() => {
+ if (!labelsSet) {
+ setLabels([]);
+ }
+ })
+ .catch((error) => {
+ console.error('Error retrieving labels:', error);
+ setLabels([]);
+ });
+}
+
+/**
+ * retrieve the list of databases in a DBMS.
+ * @param driver Neo4j driver with active session
+ * @param setDatabases callback to update the list of databases.
+ */
+export function retrieveDatabaseList(driver, setDatabases: React.Dispatch>) {
+ runCypherQuery(
+ driver,
+ 'system',
+ 'SHOW DATABASES yield name return distinct name',
+ {},
+ 1000,
+ () => {},
+ (records) => {
+ setDatabases(records.map((record) => record._fields[0]));
+ }
+ );
+}
+
+/**
+ * Updates the list of users for a given role.
+ * This is a two step operation: clear the users assigned to the role currently, and recreate them with a new list.
+ * @param driver Neo4j driver with active session.
+ * @param currentRole selected role
+ * @param allUsers list of all users.
+ * @param selectedUsers list of users to have the role after the operation completes.
+ */
+export async function updateUsers(driver, currentRole, allUsers, selectedUsers, onSuccess, onFail) {
+ // 1. Build the query that removes all users from the role.
+ let globalStatus = -1;
+ const escapedAllUsers = allUsers.map((user) => `\`${user}\``).join(',');
+ await runCypherQuery(
+ driver,
+ 'system',
+ `REVOKE ROLE ${currentRole} FROM ${escapedAllUsers}`,
+ {},
+ 1000,
+ async (status) => {
+ globalStatus = status;
+ if (globalStatus == QueryStatus.NO_DATA || globalStatus == QueryStatus.COMPLETE) {
+ // TODO: Neo4j is very slow in updating after the previous query, even though it is technically a finished query.
+ // We build in an artificial delay... This must be improved the future.
+ setTimeout(async () => {
+ if (selectedUsers.length > 0) {
+ const escapedSelectedUsers = selectedUsers.map((user) => `\`${user}\``).join(',');
+ await runCypherQuery(
+ driver,
+ 'system',
+ `GRANT ROLE ${currentRole} TO ${escapedSelectedUsers};`,
+ {},
+ 1000,
+ (status) => {
+ if (status == QueryStatus.NO_DATA || QueryStatus.COMPLETE) {
+ onSuccess();
+ }
+ }
+ );
+ } else {
+ onSuccess();
+ }
+ }, 2000);
+ }
+ },
+ (records) => {
+ if (records && records[0] && records[0].error) {
+ onFail(records[0].error);
+ }
+ }
+ );
+}
diff --git a/src/extensions/styling/StyleRuleCreationModal.tsx b/src/extensions/styling/StyleRuleCreationModal.tsx
index 0c26c4746..a295044d5 100644
--- a/src/extensions/styling/StyleRuleCreationModal.tsx
+++ b/src/extensions/styling/StyleRuleCreationModal.tsx
@@ -338,6 +338,35 @@ export const NeoCustomReportStyleModal = ({
}}
fluid
/>
+
+ e.toLowerCase().includes(rule.targetField)
+ )}
+ value={rule.targetField ? rule.targetField : (rule.field ? rule.field : '')}
+ inputValue={rule.targetField ? rule.targetField : (rule.field ? rule.field : '')}
+ popupIcon={<>>}
+ style={{ minWidth: 125, visibility: rule.customization.includes("cell") ? 'visible' : 'hidden', display: rule.customization.includes("cell") ? '' : 'none' }}
+ onInputChange={(event, value) => {
+ updateRuleField(index, 'targetField', value);
+ }}
+ onChange={(event, newValue) => {
+ updateRuleField(index, 'targetField', newValue);
+ }}
+ renderInput={(params) => (
+
+ )}
+ />
{
}
for (const [index, rule] of rules.entries()) {
// Only check customizations that are specified
- if (customizations.includes(rule.customization)) {
- // if the row contains the specified field...
- if (dict[rule.field] !== undefined && dict[rule.field] !== null) {
- const realValue = dict[rule.field].low ? dict[rule.field].low : dict[rule.field];
- const ruleValue = rule.value;
- if (evaluateCondition(realValue, rule.condition, ruleValue)) {
- return index;
- }
- }
- }
+ return evaluateSingleRuleOnDict (dict, rule, index, customizations)
}
// If no rules are met, return not found (index=-1)
return -1;
};
+export const evaluateSingleRuleOnDict = (dict, rule, ruleIndex, customizations) => {
+ if (customizations.includes(rule.customization)) {
+ // if the row contains the specified field...
+ if (dict[rule.field] !== undefined && dict[rule.field] !== null) {
+ const realValue = dict[rule.field].low ? dict[rule.field].low : dict[rule.field];
+ const ruleValue = rule.value;
+ if (evaluateCondition(realValue, rule.condition, ruleValue)) {
+ return ruleIndex;
+ }
+ }
+ }
+ return -1;
+}
+
+
/**
* Evaluates the specified rule set on a node object returned by the Neo4j driver.
* @param node - the node representation returned by the Neo4j driver.
diff --git a/src/extensions/text2cypher/component/model-examples/ExampleDisplayTable.tsx b/src/extensions/text2cypher/component/model-examples/ExampleDisplayTable.tsx
index e2e925f50..82115cbda 100644
--- a/src/extensions/text2cypher/component/model-examples/ExampleDisplayTable.tsx
+++ b/src/extensions/text2cypher/component/model-examples/ExampleDisplayTable.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import {
TrashIconOutline,
PencilSquareIconOutline,
@@ -7,6 +7,7 @@ import {
ChevronDoubleRightIconOutline,
ChevronRightIconOutline,
} from '@neo4j-ndl/react/icons';
+import ShowMoreText from 'react-show-more-text';
import {
createColumnHelper,
flexRender,
@@ -57,11 +58,15 @@ function ExampleDisplayTable({ examples, deleteModelExample, handleEdit }) {
const columns = React.useMemo(
() => [
columnHelper.accessor('question', {
- cell: (info) => info.getValue(),
+ cell: (info) => {info.getValue()} ,
header: () => 'Question',
}),
columnHelper.accessor('answer', {
- cell: (info) => info.getValue(),
+ cell: (info) => (
+
+ {info.getValue()}
+
+ ),
header: 'Answer',
}),
{
@@ -87,19 +92,52 @@ function ExampleDisplayTable({ examples, deleteModelExample, handleEdit }) {
},
});
+ const [cellWidth, setCellWidth] = useState('600px');
+
+ // For screens with 1080 x pixels or less
+ useEffect(() => {
+ const updateCellWidth = () => {
+ if (window.innerWidth <= 1080) {
+ // Example breakpoint for smaller screens
+ setCellWidth('463px');
+ } else {
+ setCellWidth('600px');
+ }
+ };
+
+ window.addEventListener('resize', updateCellWidth);
+ updateCellWidth(); // Initialize on component mount
+
+ return () => window.removeEventListener('resize', updateCellWidth);
+ }, []);
+
return (
-
+
{table.getHeaderGroups().map((headerGroup) => (
{headerGroup.headers.map((header) => (
-
+
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
))}
@@ -110,11 +148,23 @@ function ExampleDisplayTable({ examples, deleteModelExample, handleEdit }) {
{table.getRowModel().rows.map((row) => (
{row.getVisibleCells().map((cell) => (
-
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
))}
diff --git a/src/index.pcss b/src/index.pcss
index ad0fea17c..c5c637c9d 100644
--- a/src/index.pcss
+++ b/src/index.pcss
@@ -37,6 +37,10 @@
@apply n-mr-token-2 n-w-4 n-h-4;
}
+ .btn-icon-base-r-m {
+ @apply n-ml-token-3 n-w-5 n-h-5;
+ }
+
.btn-icon-base-r {
@apply n-ml-token-3 n-w-6 n-h-6;
}
@@ -70,6 +74,11 @@
@apply n-ml-token-3;
}
+ /* DataGrid overrides */
+ .MuiDataGrid-footerContainer > div {
+ @apply n-mt-0;
+ }
+
/* Make bullet list points in Markdown card view */
.card-view ul {
@apply n-list-disc n-ml-token-7;
@@ -86,4 +95,22 @@
.n-bg-dark-neutral-text-weak {
background-color: rgb(196 200 205 / var(--tw-bg-opacity)) !important;
}
+
+ /* Markdown table styles */
+ .markdown-table {
+ width: 100%;
+ border-collapse: collapse;
+ }
+
+ .markdown-table th,
+ .markdown-table td {
+ border: 1px solid #ddd; /* Light gray border */
+ padding: 8px; /* Padding around text */
+ text-align: left; /* Align text to the left */
+ }
+
+ .markdown-table th {
+ background-color: #f4f4f4; /* Light gray background for header */
+ color: #333; /* Dark text color for contrast */
+ }
}
diff --git a/src/modal/AboutModal.tsx b/src/modal/AboutModal.tsx
index fd7af427c..62d4816c0 100644
--- a/src/modal/AboutModal.tsx
+++ b/src/modal/AboutModal.tsx
@@ -3,7 +3,7 @@ import { Button, Dialog, TextLink } from '@neo4j-ndl/react';
import { BookOpenIconOutline, BeakerIconOutline } from '@neo4j-ndl/react/icons';
import { Section, SectionTitle, SectionContent } from './ModalUtils';
-export const version = '2.4.2';
+export const version = '2.4.9-labs';
export const NeoAboutModal = ({ open, handleClose, getDebugState }) => {
const downloadDebugFile = () => {
diff --git a/src/modal/ExportModal.tsx b/src/modal/ExportModal.tsx
index 7040ede4a..bb1d76fe0 100644
--- a/src/modal/ExportModal.tsx
+++ b/src/modal/ExportModal.tsx
@@ -14,7 +14,7 @@ export const NeoExportModal = ({ dashboard }) => {
return (
<>
- setOpen(true)} aria-label='Export' title='Export'>
+ setOpen(true)} aria-label='Export'>
diff --git a/src/report/ReportQueryRunner.ts b/src/report/ReportQueryRunner.ts
index 4259b66e6..599e4afde 100644
--- a/src/report/ReportQueryRunner.ts
+++ b/src/report/ReportQueryRunner.ts
@@ -65,6 +65,7 @@ export async function runCypherQuery(
setStatus(QueryStatus.ERROR);
return;
}
+
const session = database ? driver.session({ database: database }) : driver.session();
const transaction = session.beginTransaction({ timeout: queryTimeLimit * 1000, connectionTimeout: 2000 });
diff --git a/src/report/ReportRecordProcessing.tsx b/src/report/ReportRecordProcessing.tsx
index 1109d1e3a..cae643d1e 100644
--- a/src/report/ReportRecordProcessing.tsx
+++ b/src/report/ReportRecordProcessing.tsx
@@ -10,6 +10,7 @@ import {
valueIsPath,
valueIsRelationship,
} from '../chart/ChartUtils';
+// import DOMPurify from 'dompurify';
/**
* Collects all node labels and node properties in a set of Neo4j records.
@@ -245,15 +246,40 @@ function RenderPath(value) {
});
}
-function RenderArray(value) {
- const mapped = value.map((v, i) => {
- return (
-
- {RenderSubValue(v)}
- {i < value.length - 1 && !valueIsNode(v) && !valueIsRelationship(v) ? , : <>>}
-
- );
- });
+/**
+ * Renders an array of values.
+ *
+ * @param value - The array of values to render.
+ * @param transposedTable - Optional. Specifies whether the table should be transposed. Default is false.
+ * @returns The rendered array of values.
+ */
+function RenderArray(value, transposedTable = false) {
+ let mapped = [];
+ // If the first value is neither a Node nor a Relationship object
+ // It is safe to assume that all values should be renedered as strings
+ if (value.length > 0 && !valueIsNode(value[0]) && !valueIsRelationship(value[0])) {
+ // If this request comes up from a transposed table
+ // The returned value must be a single value, not an array
+ // Otherwise, it will cast to [Object object], [Object object]
+ if (transposedTable) {
+ return RenderString(value.join(', '));
+ }
+ // Nominal case of a list of values renderable as strings
+ // These should be joined by commas, and not inside tags
+ mapped = value.map((v, i) => {
+ return RenderSubValue(v) + (i < value.length - 1 ? ', ' : '');
+ });
+ } else {
+ // Render Node and Relationship objects, which will look like a Path
+ mapped = value.map((v, i) => {
+ return (
+
+ {RenderSubValue(v)}
+ {i < value.length - 1 && !valueIsNode(v) && !valueIsRelationship(v) ? , : <>>}
+
+ );
+ });
+ }
return mapped;
}
@@ -316,7 +342,7 @@ function RenderNumber(value) {
return number;
}
-export function RenderSubValue(value) {
+export function RenderSubValue(value, transposedTable = false) {
if (value == undefined) {
return '';
}
@@ -324,7 +350,7 @@ export function RenderSubValue(value) {
const columnProperties = rendererForType[type];
if (columnProperties) {
if (columnProperties.renderValue) {
- return columnProperties.renderValue({ value: value });
+ return columnProperties.renderValue({ value: value, transposedTable: transposedTable });
} else if (columnProperties.valueGetter) {
return columnProperties.valueGetter({ value: value });
}
@@ -362,7 +388,7 @@ export const rendererForType: any = {
},
array: {
type: 'string',
- renderValue: (c) => RenderArray(c.value),
+ renderValue: (c) => RenderArray(c.value, c.transposedTable),
},
string: {
type: 'string',
diff --git a/src/settings/SettingsThunks.ts b/src/settings/SettingsThunks.ts
index 267911ae5..319993a1a 100644
--- a/src/settings/SettingsThunks.ts
+++ b/src/settings/SettingsThunks.ts
@@ -1,6 +1,6 @@
import { setSessionParameters } from '../application/ApplicationActions';
import { hardResetCardSettings } from '../card/CardActions';
-import { castToNeo4jDate, isCastableToNeo4jDate, valueIsNode } from '../chart/ChartUtils';
+import { castToNeo4jDate, isCastableToNeo4jDate, toNumber, valueIsNode } from '../chart/ChartUtils';
import { createNotificationThunk } from '../page/PageThunks';
import { updateDashboardSetting } from './SettingsActions';
@@ -61,7 +61,6 @@ export const updateGlobalParametersThunk = (newParameters) => (dispatch: any, ge
try {
const { settings } = getState().dashboard;
const parameters = settings.parameters ? settings.parameters : {};
-
// if new parameters are set...
if (newParameters) {
// iterate over the key value pairs in parameters
@@ -73,6 +72,7 @@ export const updateGlobalParametersThunk = (newParameters) => (dispatch: any, ge
}
});
dispatch(updateDashboardSetting('parameters', { ...parameters }));
+ dispatch(updateParametersToNeo4jTypeThunk());
}
} catch (e) {
dispatch(createNotificationThunk('Unable to update global parameters', e));
@@ -86,12 +86,17 @@ export const updateParametersToNeo4jTypeThunk = () => (dispatch: any, getState:
try {
const { settings } = getState().dashboard;
const parameters = settings.parameters ? settings.parameters : {};
-
// if new parameters are set...
// iterate over the key value pairs in parameters
Object.keys(parameters).forEach((key) => {
if (isCastableToNeo4jDate(parameters[key])) {
parameters[key] = castToNeo4jDate(parameters[key]);
+ } else if (
+ parameters[key] &&
+ !isNaN(toNumber(parameters[key])) &&
+ typeof toNumber(parameters[key]) === 'number'
+ ) {
+ parameters[key] = toNumber(parameters[key]);
} else if (parameters[key] == undefined) {
delete parameters[key];
}
diff --git a/src/utils/parameterUtils.ts b/src/utils/parameterUtils.ts
new file mode 100644
index 000000000..602711b2f
--- /dev/null
+++ b/src/utils/parameterUtils.ts
@@ -0,0 +1,35 @@
+/**
+ * Extracts all parameter names from a given Cypher query string.
+ *
+ * @param {string} cypherQuery The Cypher query string to extract parameter names from.
+ * @returns {string[]} An array containing all extracted parameter names.
+ */
+export const extractAllParameterNames = (cypherQuery: string): string[] => {
+ // A regular expression pattern to match parameter names following '$'
+ const pattern = /\$([A-Za-z_]\w*)/g;
+
+ const parameterNames: string[] = [];
+ let match: any;
+
+ while ((match = pattern.exec(cypherQuery)) !== null) {
+ parameterNames.push(match[1]);
+ }
+
+ return parameterNames;
+}
+
+/**
+ * Checks if all parameter names are present in the global parameter names.
+ *
+ * @param {string[]} parameterNames An array of parameter names to be checked.
+ * @param {object} globalParameterNames The object containing global parameter names to compare against.
+ * @returns {boolean} A boolean indicating whether all parameters are present in the global parameters.
+ */
+export const checkParametersNameInGlobalParameter = (parameterNames: string[], globalParameterNames: any): boolean => {
+ for (const key of parameterNames) {
+ if (!globalParameterNames[key] || (Array.isArray(globalParameterNames[key]) && globalParameterNames[key].length === 0) || globalParameterNames[key] === '') {
+ return true;
+ }
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/ssl/Dockerfile b/ssl/Dockerfile
new file mode 100644
index 000000000..3e362b4de
--- /dev/null
+++ b/ssl/Dockerfile
@@ -0,0 +1,25 @@
+# build stage
+FROM neo4jlabs/neodash:latest AS neodash
+
+ENV NGINX_HTTPS_PORT=5443
+
+USER root
+
+RUN mkdir -p /etc/nginx/certs
+
+RUN --mount=type=secret,id=NEODASH_SSL_KEY \
+ base64 -d /run/secrets/NEODASH_SSL_KEY > /etc/nginx/certs/key.pem
+
+RUN --mount=type=secret,id=NEODASH_SSL_CERT \
+ base64 -d /run/secrets/NEODASH_SSL_CERT > /etc/nginx/certs/cert.pem
+
+COPY default.conf /etc/nginx/templates/default.conf.template
+COPY default.conf /etc/nginx/conf.d/
+
+RUN chown -R nginx:nginx /etc/nginx
+
+USER nginx
+EXPOSE $NGINX_HTTPS_PORT
+
+HEALTHCHECK CMD curl --fail "https://localhost:$NGINX_HTTPS_PORT" || exit 1
+LABEL version="1.0"
\ No newline at end of file
diff --git a/ssl/default.conf b/ssl/default.conf
new file mode 100644
index 000000000..547c4b214
--- /dev/null
+++ b/ssl/default.conf
@@ -0,0 +1,34 @@
+server {
+ listen ${NGINX_PORT};
+ server_name localhost;
+ include mime.types;
+ location / {
+ root /usr/share/nginx/html;
+ try_files $uri $uri/ /index.html;
+ index index.html index.htm;
+ }
+ # redirect server error pages to the static page /50x.html
+ # Note: This is optional, depending on the implementation in React
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
+server {
+ listen 5443 ssl;
+ ssl_certificate /etc/nginx/certs/cert.pem;
+ ssl_certificate_key /etc/nginx/certs/key.pem;
+ server_name localhost;
+ include mime.types;
+ location / {
+ root /usr/share/nginx/html;
+ try_files $uri $uri/ /index.html;
+ index index.html index.htm;
+ }
+ # redirect server error pages to the static page /50x.html
+ # Note: This is optional, depending on the implementation in React
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index fcb9b0188..ecf250f63 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1306,6 +1306,20 @@
dependencies:
regenerator-runtime "^0.14.0"
+"@babel/runtime@^7.23.9":
+ version "7.23.9"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
+ integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
+ dependencies:
+ regenerator-runtime "^0.14.0"
+
+"@babel/runtime@^7.24.0":
+ version "7.24.5"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.5.tgz#230946857c053a36ccc66e1dd03b17dd0c4ed02c"
+ integrity sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==
+ dependencies:
+ regenerator-runtime "^0.14.0"
+
"@babel/template@^7.18.10", "@babel/template@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
@@ -1919,7 +1933,7 @@
"@emotion/weak-memoize" "^0.3.0"
stylis "4.1.3"
-"@emotion/cache@^11.10.8":
+"@emotion/cache@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff"
integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==
@@ -1935,6 +1949,11 @@
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7"
integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==
+"@emotion/hash@^0.9.1":
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43"
+ integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==
+
"@emotion/is-prop-valid@^1.1.0", "@emotion/is-prop-valid@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83"
@@ -2068,6 +2087,13 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
+"@floating-ui/core@^1.0.0":
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1"
+ integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==
+ dependencies:
+ "@floating-ui/utils" "^0.2.1"
+
"@floating-ui/core@^1.2.4":
version "1.2.5"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.5.tgz#612f0d203e6f647490d572c7b798eebac9e3cf54"
@@ -2092,6 +2118,14 @@
dependencies:
"@floating-ui/core" "^1.3.0"
+"@floating-ui/dom@^1.6.1":
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef"
+ integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==
+ dependencies:
+ "@floating-ui/core" "^1.0.0"
+ "@floating-ui/utils" "^0.2.0"
+
"@floating-ui/react-dom@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.1.tgz#7972a4fc488a8c746cded3cfe603b6057c308a91"
@@ -2099,6 +2133,13 @@
dependencies:
"@floating-ui/dom" "^1.3.0"
+"@floating-ui/react-dom@^2.0.8":
+ version "2.0.8"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.8.tgz#afc24f9756d1b433e1fe0d047c24bd4d9cefaa5d"
+ integrity sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==
+ dependencies:
+ "@floating-ui/dom" "^1.6.1"
+
"@floating-ui/react@^0.24.2":
version "0.24.3"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.24.3.tgz#4f11f09c7245555724f5167dd6925133457db89c"
@@ -2108,6 +2149,11 @@
aria-hidden "^1.1.3"
tabbable "^6.0.1"
+"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2"
+ integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
+
"@formatjs/ecma402-abstract@1.17.0":
version "1.17.0"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.0.tgz#2ce191a3bde4c65c6684e03fa247062a4a294b9e"
@@ -2259,23 +2305,42 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
+"@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+ integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
+ dependencies:
+ "@jridgewell/set-array" "^1.2.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
"@jridgewell/resolve-uri@3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+"@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
-"@jridgewell/source-map@^0.3.2":
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
- integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+"@jridgewell/set-array@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
+ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
+"@jridgewell/source-map@^0.3.3":
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a"
+ integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
dependencies:
- "@jridgewell/gen-mapping" "^0.3.0"
- "@jridgewell/trace-mapping" "^0.3.9"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.14"
@@ -2287,13 +2352,10 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
-"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9":
- version "0.3.17"
- resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
- integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
- dependencies:
- "@jridgewell/resolve-uri" "3.1.0"
- "@jridgewell/sourcemap-codec" "1.4.14"
+"@jridgewell/sourcemap-codec@^1.4.14":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
+ integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
"@jridgewell/trace-mapping@^0.3.17":
version "0.3.18"
@@ -2303,6 +2365,22 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+ version "0.3.25"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+ integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9":
+ version "0.3.17"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
+ integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
+ dependencies:
+ "@jridgewell/resolve-uri" "3.1.0"
+ "@jridgewell/sourcemap-codec" "1.4.14"
+
"@leichtgewicht/ip-codec@^2.0.1":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
@@ -2360,74 +2438,92 @@
"@lezer/common" "^1.0.0"
"@lezer/highlight" "^1.0.0"
-"@mui/base@5.0.0-alpha.128":
- version "5.0.0-alpha.128"
- resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.128.tgz#8ce4beb971ac989df0b1d3b2bd3e9274dbfa604f"
- integrity sha512-wub3wxNN+hUp8hzilMlXX3sZrPo75vsy1cXEQpqdTfIFlE9HprP1jlulFiPg5tfPst2OKmygXr2hhmgvAKRrzQ==
+"@mui/base@5.0.0-beta.37":
+ version "5.0.0-beta.37"
+ resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.37.tgz#0e7e0f28402391fcfbb05476d5acc6c4f2d817b1"
+ integrity sha512-/o3anbb+DeCng8jNsd3704XtmmLDZju1Fo8R2o7ugrVtPQ/QpcqddwKNzKPZwa0J5T8YNW3ZVuHyQgbTnQLisQ==
dependencies:
- "@babel/runtime" "^7.21.0"
- "@emotion/is-prop-valid" "^1.2.0"
- "@mui/types" "^7.2.4"
- "@mui/utils" "^5.12.3"
- "@popperjs/core" "^2.11.7"
- clsx "^1.2.1"
+ "@babel/runtime" "^7.23.9"
+ "@floating-ui/react-dom" "^2.0.8"
+ "@mui/types" "^7.2.13"
+ "@mui/utils" "^5.15.11"
+ "@popperjs/core" "^2.11.8"
+ clsx "^2.1.0"
prop-types "^15.8.1"
- react-is "^18.2.0"
-"@mui/core-downloads-tracker@^5.12.3":
- version "5.12.3"
- resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.3.tgz#3dffe62dccc065ddd7338e97d7be4b917004287e"
- integrity sha512-yiJZ+knaknPHuRKhRk4L6XiwppwkAahVal3LuYpvBH7GkA2g+D9WLEXOEnNYtVFUggyKf6fWGLGnx0iqzkU5YA==
+"@mui/core-downloads-tracker@^5.15.11":
+ version "5.15.11"
+ resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.11.tgz#dcaf6156880e81e4547237fb781700485453e964"
+ integrity sha512-JVrJ9Jo4gyU707ujnRzmE8ABBWpXd6FwL9GYULmwZRtfPg89ggXs/S3MStQkpJ1JRWfdLL6S5syXmgQGq5EDAw==
"@mui/material@^5.12.3":
- version "5.12.3"
- resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.3.tgz#398c1b123fb065763558bc1f9fc47d1f8cb87d0c"
- integrity sha512-xNmKlrEN4HsTaKFNLZfc7ie7CXx2YqEeO//hsXZx2p3MGtDdeMr2sV3jC4hsFs57RhQlF79weY7uVvC8xSuVbg==
- dependencies:
- "@babel/runtime" "^7.21.0"
- "@mui/base" "5.0.0-alpha.128"
- "@mui/core-downloads-tracker" "^5.12.3"
- "@mui/system" "^5.12.3"
- "@mui/types" "^7.2.4"
- "@mui/utils" "^5.12.3"
- "@types/react-transition-group" "^4.4.5"
- clsx "^1.2.1"
- csstype "^3.1.2"
+ version "5.15.11"
+ resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.11.tgz#4f42ee30443699ffb5836029c6d8464154eca603"
+ integrity sha512-FA3eEuEZaDaxgN3CgfXezMWbCZ4VCeU/sv0F0/PK5n42qIgsPVD6q+j71qS7/62sp6wRFMHtDMpXRlN+tT/7NA==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@mui/base" "5.0.0-beta.37"
+ "@mui/core-downloads-tracker" "^5.15.11"
+ "@mui/system" "^5.15.11"
+ "@mui/types" "^7.2.13"
+ "@mui/utils" "^5.15.11"
+ "@types/react-transition-group" "^4.4.10"
+ clsx "^2.1.0"
+ csstype "^3.1.3"
prop-types "^15.8.1"
react-is "^18.2.0"
react-transition-group "^4.4.5"
-"@mui/private-theming@^5.12.3":
- version "5.12.3"
- resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.12.3.tgz#f5e4704e25d9d91b906561cae573cda8f3801e10"
- integrity sha512-o1e7Z1Bp27n4x2iUHhegV4/Jp6H3T6iBKHJdLivS5GbwsuAE/5l4SnZ+7+K+e5u9TuhwcAKZLkjvqzkDe8zqfA==
+"@mui/private-theming@^5.15.11":
+ version "5.15.11"
+ resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.11.tgz#4b9289b56b1ae0beb84e47bc9952f927b6e175ae"
+ integrity sha512-jY/696SnSxSzO1u86Thym7ky5T9CgfidU3NFJjguldqK4f3Z5S97amZ6nffg8gTD0HBjY9scB+4ekqDEUmxZOA==
dependencies:
- "@babel/runtime" "^7.21.0"
- "@mui/utils" "^5.12.3"
+ "@babel/runtime" "^7.23.9"
+ "@mui/utils" "^5.15.11"
prop-types "^15.8.1"
-"@mui/styled-engine@^5.12.3":
- version "5.12.3"
- resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.12.3.tgz#3307643d52c81947a624cdd0437536cc8109c4f0"
- integrity sha512-AhZtiRyT8Bjr7fufxE/mLS+QJ3LxwX1kghIcM2B2dvJzSSg9rnIuXDXM959QfUVIM3C8U4x3mgVoPFMQJvc4/g==
+"@mui/private-theming@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.14.tgz#edd9a82948ed01586a01c842eb89f0e3f68970ee"
+ integrity sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==
dependencies:
- "@babel/runtime" "^7.21.0"
- "@emotion/cache" "^11.10.8"
- csstype "^3.1.2"
+ "@babel/runtime" "^7.23.9"
+ "@mui/utils" "^5.15.14"
prop-types "^15.8.1"
-"@mui/styles@^5.12.3":
- version "5.12.3"
- resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.12.3.tgz#2856fea1002199155bca02d4188b3771539481c6"
- integrity sha512-y0GN1kTYO2FF/0LH8a0PpVxwLotlcunFqdJpCL5gza0w5Fqz9wxlwauPZW0bDt0+sF79CrohzdzWkh+fxB+oww==
+"@mui/styled-engine@^5.15.11":
+ version "5.15.11"
+ resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.11.tgz#040181f31910e0f66d43a5c44fe89da06b34212b"
+ integrity sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==
dependencies:
- "@babel/runtime" "^7.21.0"
- "@emotion/hash" "^0.9.0"
- "@mui/private-theming" "^5.12.3"
- "@mui/types" "^7.2.4"
- "@mui/utils" "^5.12.3"
- clsx "^1.2.1"
- csstype "^3.1.2"
+ "@babel/runtime" "^7.23.9"
+ "@emotion/cache" "^11.11.0"
+ csstype "^3.1.3"
+ prop-types "^15.8.1"
+
+"@mui/styled-engine@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.14.tgz#168b154c4327fa4ccc1933a498331d53f61c0de2"
+ integrity sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@emotion/cache" "^11.11.0"
+ csstype "^3.1.3"
+ prop-types "^15.8.1"
+
+"@mui/styles@^5.12.3":
+ version "5.15.11"
+ resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.15.11.tgz#2fc57a42eff47542924e1ba90fb188b733d295aa"
+ integrity sha512-7TCs+0AGCtNaqBHhj0ZODYLnQjVrY9nG4PrT2bzIGIh3zvJxF7zY6IRiPyBFsKY1OjdVHjjYuan4U81QbdBrew==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@emotion/hash" "^0.9.1"
+ "@mui/private-theming" "^5.15.11"
+ "@mui/types" "^7.2.13"
+ "@mui/utils" "^5.15.11"
+ clsx "^2.1.0"
+ csstype "^3.1.3"
hoist-non-react-statics "^3.3.2"
jss "^10.10.0"
jss-plugin-camel-case "^10.10.0"
@@ -2439,24 +2535,43 @@
jss-plugin-vendor-prefixer "^10.10.0"
prop-types "^15.8.1"
-"@mui/system@^5.12.3":
- version "5.12.3"
- resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.3.tgz#306b3cdffa3046067640219c1e5dd7e3dae38ff9"
- integrity sha512-JB/6sypHqeJCqwldWeQ1MKkijH829EcZAKKizxbU2MJdxGG5KSwZvTBa5D9qiJUA1hJFYYupjiuy9ZdJt6rV6w==
- dependencies:
- "@babel/runtime" "^7.21.0"
- "@mui/private-theming" "^5.12.3"
- "@mui/styled-engine" "^5.12.3"
- "@mui/types" "^7.2.4"
- "@mui/utils" "^5.12.3"
- clsx "^1.2.1"
- csstype "^3.1.2"
+"@mui/system@^5.15.11":
+ version "5.15.11"
+ resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.11.tgz#19cf1974f82f1dd38be1f162034efecadd765733"
+ integrity sha512-9j35suLFq+MgJo5ktVSHPbkjDLRMBCV17NMBdEQurh6oWyGnLM4uhU4QGZZQ75o0vuhjJghOCA1jkO3+79wKsA==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@mui/private-theming" "^5.15.11"
+ "@mui/styled-engine" "^5.15.11"
+ "@mui/types" "^7.2.13"
+ "@mui/utils" "^5.15.11"
+ clsx "^2.1.0"
+ csstype "^3.1.3"
prop-types "^15.8.1"
-"@mui/types@^7.2.4":
- version "7.2.4"
- resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328"
- integrity sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==
+"@mui/system@^5.15.14":
+ version "5.15.15"
+ resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.15.tgz#658771b200ce3c4a0f28e58169f02e5e718d1c53"
+ integrity sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@mui/private-theming" "^5.15.14"
+ "@mui/styled-engine" "^5.15.14"
+ "@mui/types" "^7.2.14"
+ "@mui/utils" "^5.15.14"
+ clsx "^2.1.0"
+ csstype "^3.1.3"
+ prop-types "^15.8.1"
+
+"@mui/types@^7.2.13":
+ version "7.2.13"
+ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.13.tgz#d1584912942f9dc042441ecc2d1452be39c666b8"
+ integrity sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==
+
+"@mui/types@^7.2.14":
+ version "7.2.14"
+ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.14.tgz#8a02ac129b70f3d82f2f9b76ded2c8d48e3fc8c9"
+ integrity sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==
"@mui/utils@^5.10.3":
version "5.11.9"
@@ -2469,27 +2584,37 @@
prop-types "^15.8.1"
react-is "^18.2.0"
-"@mui/utils@^5.12.3":
- version "5.12.3"
- resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.12.3.tgz#3fa3570dac7ec66bb9cc84ab7c16ab6e1b7200f2"
- integrity sha512-D/Z4Ub3MRl7HiUccid7sQYclTr24TqUAQFFlxHQF8FR177BrCTQ0JJZom7EqYjZCdXhwnSkOj2ph685MSKNtIA==
+"@mui/utils@^5.15.11":
+ version "5.15.11"
+ resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.11.tgz#a71804d6d6025783478fd1aca9afbf83d9b789c7"
+ integrity sha512-D6bwqprUa9Stf8ft0dcMqWyWDKEo7D+6pB1k8WajbqlYIRA8J8Kw9Ra7PSZKKePGBGWO+/xxrX1U8HpG/aXQCw==
dependencies:
- "@babel/runtime" "^7.21.0"
- "@types/prop-types" "^15.7.5"
- "@types/react-is" "^16.7.1 || ^17.0.0"
+ "@babel/runtime" "^7.23.9"
+ "@types/prop-types" "^15.7.11"
prop-types "^15.8.1"
react-is "^18.2.0"
-"@mui/x-data-grid@5.17.26":
- version "5.17.26"
- resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.26.tgz#1f7fa73dd3986cf052e2fd2cb56eb4678a7bd913"
- integrity sha512-eGJq9J0g9cDGLFfMmugOadZx0mJeOd/yQpHwEa5gUXyONS6qF0OhXSWyDOhDdA3l2TOoQzotMN5dY/T4Wl1KYA==
+"@mui/utils@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.14.tgz#e414d7efd5db00bfdc875273a40c0a89112ade3a"
+ integrity sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==
dependencies:
- "@babel/runtime" "^7.18.9"
- "@mui/utils" "^5.10.3"
- clsx "^1.2.1"
+ "@babel/runtime" "^7.23.9"
+ "@types/prop-types" "^15.7.11"
prop-types "^15.8.1"
- reselect "^4.1.6"
+ react-is "^18.2.0"
+
+"@mui/x-data-grid@7.4.0":
+ version "7.4.0"
+ resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-7.4.0.tgz#1901f2908aca760146ccae74b064fc15462bcf63"
+ integrity sha512-ILu0AVqqHQf4wN/nblsJ/k7PkvlB115vQ/FEiYk7neZlc/kOJOUyst3MWMVClAecZ8+JEs476q40xd4r1CtMfw==
+ dependencies:
+ "@babel/runtime" "^7.24.0"
+ "@mui/system" "^5.15.14"
+ "@mui/utils" "^5.15.14"
+ clsx "^2.1.1"
+ prop-types "^15.8.1"
+ reselect "^4.1.8"
"@mui/x-date-pickers@^5.0.17":
version "5.0.19"
@@ -2922,11 +3047,6 @@
schema-utils "^3.0.0"
source-map "^0.7.3"
-"@popperjs/core@^2.11.7":
- version "2.11.7"
- resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7"
- integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==
-
"@popperjs/core@^2.11.8":
version "2.11.8"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
@@ -4459,31 +4579,10 @@
dependencies:
"@types/ms" "*"
-"@types/eslint-scope@^3.7.3":
- version "3.7.4"
- resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
- integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==
- dependencies:
- "@types/eslint" "*"
- "@types/estree" "*"
-
-"@types/eslint@*":
- version "8.21.1"
- resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.1.tgz#110b441a210d53ab47795124dbc3e9bb993d1e7c"
- integrity sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==
- dependencies:
- "@types/estree" "*"
- "@types/json-schema" "*"
-
-"@types/estree@*":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
- integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==
-
-"@types/estree@^0.0.51":
- version "0.0.51"
- resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
- integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
+"@types/estree@^1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
+ integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33":
version "4.17.33"
@@ -4563,7 +4662,7 @@
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3"
integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==
-"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.11"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
@@ -4617,6 +4716,11 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+"@types/prop-types@^15.7.11":
+ version "15.7.11"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563"
+ integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==
+
"@types/qs@*":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
@@ -4658,6 +4762,13 @@
dependencies:
"@types/react" "*"
+"@types/react-transition-group@^4.4.10":
+ version "4.4.10"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac"
+ integrity sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*":
version "18.0.28"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065"
@@ -4848,125 +4959,125 @@
"@typescript-eslint/types" "5.52.0"
eslint-visitor-keys "^3.3.0"
-"@webassemblyjs/ast@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
- integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
+"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb"
+ integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==
dependencies:
- "@webassemblyjs/helper-numbers" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/helper-numbers" "1.11.6"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
-"@webassemblyjs/floating-point-hex-parser@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
- integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
+"@webassemblyjs/floating-point-hex-parser@1.11.6":
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431"
+ integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==
-"@webassemblyjs/helper-api-error@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
- integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
+"@webassemblyjs/helper-api-error@1.11.6":
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768"
+ integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==
-"@webassemblyjs/helper-buffer@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
- integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
+"@webassemblyjs/helper-buffer@1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6"
+ integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==
-"@webassemblyjs/helper-numbers@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
- integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
+"@webassemblyjs/helper-numbers@1.11.6":
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5"
+ integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==
dependencies:
- "@webassemblyjs/floating-point-hex-parser" "1.11.1"
- "@webassemblyjs/helper-api-error" "1.11.1"
+ "@webassemblyjs/floating-point-hex-parser" "1.11.6"
+ "@webassemblyjs/helper-api-error" "1.11.6"
"@xtuc/long" "4.2.2"
-"@webassemblyjs/helper-wasm-bytecode@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
- integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
+"@webassemblyjs/helper-wasm-bytecode@1.11.6":
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9"
+ integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==
-"@webassemblyjs/helper-wasm-section@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
- integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
+"@webassemblyjs/helper-wasm-section@1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf"
+ integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==
dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
+ "@webassemblyjs/ast" "1.12.1"
+ "@webassemblyjs/helper-buffer" "1.12.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
+ "@webassemblyjs/wasm-gen" "1.12.1"
-"@webassemblyjs/ieee754@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
- integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
+"@webassemblyjs/ieee754@1.11.6":
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a"
+ integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==
dependencies:
"@xtuc/ieee754" "^1.2.0"
-"@webassemblyjs/leb128@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
- integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
+"@webassemblyjs/leb128@1.11.6":
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7"
+ integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==
dependencies:
"@xtuc/long" "4.2.2"
-"@webassemblyjs/utf8@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
- integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
-
-"@webassemblyjs/wasm-edit@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
- integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/helper-wasm-section" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
- "@webassemblyjs/wasm-opt" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
- "@webassemblyjs/wast-printer" "1.11.1"
-
-"@webassemblyjs/wasm-gen@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
- integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/ieee754" "1.11.1"
- "@webassemblyjs/leb128" "1.11.1"
- "@webassemblyjs/utf8" "1.11.1"
-
-"@webassemblyjs/wasm-opt@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
- integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
-
-"@webassemblyjs/wasm-parser@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
- integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-api-error" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/ieee754" "1.11.1"
- "@webassemblyjs/leb128" "1.11.1"
- "@webassemblyjs/utf8" "1.11.1"
-
-"@webassemblyjs/wast-printer@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
- integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
+"@webassemblyjs/utf8@1.11.6":
+ version "1.11.6"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a"
+ integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==
+
+"@webassemblyjs/wasm-edit@^1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b"
+ integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==
+ dependencies:
+ "@webassemblyjs/ast" "1.12.1"
+ "@webassemblyjs/helper-buffer" "1.12.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
+ "@webassemblyjs/helper-wasm-section" "1.12.1"
+ "@webassemblyjs/wasm-gen" "1.12.1"
+ "@webassemblyjs/wasm-opt" "1.12.1"
+ "@webassemblyjs/wasm-parser" "1.12.1"
+ "@webassemblyjs/wast-printer" "1.12.1"
+
+"@webassemblyjs/wasm-gen@1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547"
+ integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==
+ dependencies:
+ "@webassemblyjs/ast" "1.12.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
+ "@webassemblyjs/ieee754" "1.11.6"
+ "@webassemblyjs/leb128" "1.11.6"
+ "@webassemblyjs/utf8" "1.11.6"
+
+"@webassemblyjs/wasm-opt@1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5"
+ integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==
+ dependencies:
+ "@webassemblyjs/ast" "1.12.1"
+ "@webassemblyjs/helper-buffer" "1.12.1"
+ "@webassemblyjs/wasm-gen" "1.12.1"
+ "@webassemblyjs/wasm-parser" "1.12.1"
+
+"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937"
+ integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.12.1"
+ "@webassemblyjs/helper-api-error" "1.11.6"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
+ "@webassemblyjs/ieee754" "1.11.6"
+ "@webassemblyjs/leb128" "1.11.6"
+ "@webassemblyjs/utf8" "1.11.6"
+
+"@webassemblyjs/wast-printer@1.12.1":
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac"
+ integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==
+ dependencies:
+ "@webassemblyjs/ast" "1.12.1"
"@xtuc/long" "4.2.2"
"@webpack-cli/configtest@^1.2.0":
@@ -5019,25 +5130,20 @@ accessor-fn@1:
resolved "https://registry.yarnpkg.com/accessor-fn/-/accessor-fn-1.4.1.tgz#ff6555ba91bbae1ba8e469bfdca6e7ee74dccc78"
integrity sha512-P7yNKfmpuWLUwiRVk9RkRIPGjngemjZ7yANc0DL7otgDqEIWkEByMhShzfgQ5ZwCPEUmba4v1kOqCdGhpzY3ew==
-acorn-import-assertions@^1.7.6:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
- integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
+acorn-import-attributes@^1.9.5:
+ version "1.9.5"
+ resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef"
+ integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==
acorn-jsx@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0:
- version "8.8.2"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
- integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
-
-acorn@^8.8.1:
- version "8.10.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
- integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
+acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1, acorn@^8.8.2:
+ version "8.12.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
+ integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
agent-base@6:
version "6.0.2"
@@ -5502,13 +5608,13 @@ bluebird@^3.7.2:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
-body-parser@1.20.1:
- version "1.20.1"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
- integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
+body-parser@1.20.2:
+ version "1.20.2"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
+ integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
dependencies:
bytes "3.1.2"
- content-type "~1.0.4"
+ content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
@@ -5516,7 +5622,7 @@ body-parser@1.20.1:
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
- raw-body "2.5.1"
+ raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@@ -5552,25 +5658,15 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
-browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4.21.5:
- version "4.21.5"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7"
- integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==
- dependencies:
- caniuse-lite "^1.0.30001449"
- electron-to-chromium "^1.4.284"
- node-releases "^2.0.8"
- update-browserslist-db "^1.0.10"
-
-browserslist@^4.21.9:
- version "4.21.9"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635"
- integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==
+browserslist@^4.21.10, browserslist@^4.21.3, browserslist@^4.21.5, browserslist@^4.21.9:
+ version "4.23.3"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800"
+ integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==
dependencies:
- caniuse-lite "^1.0.30001503"
- electron-to-chromium "^1.4.431"
- node-releases "^2.0.12"
- update-browserslist-db "^1.0.11"
+ caniuse-lite "^1.0.30001646"
+ electron-to-chromium "^1.5.4"
+ node-releases "^2.0.18"
+ update-browserslist-db "^1.1.0"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
@@ -5692,11 +5788,16 @@ camelize@^1.0.0:
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3"
integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==
-caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001503:
+caniuse-lite@^1.0.30001464:
version "1.0.30001546"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz"
integrity sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==
+caniuse-lite@^1.0.30001646:
+ version "1.0.30001655"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz#0ce881f5a19a2dcfda2ecd927df4d5c1684b982f"
+ integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==
+
canvas-color-tracker@1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/canvas-color-tracker/-/canvas-color-tracker-1.2.1.tgz#c552872f8f254bac3e74ea4cc7fed3bb19859bf1"
@@ -5925,6 +6026,16 @@ clsx@^1.1.1, clsx@^1.2.1:
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
+clsx@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb"
+ integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==
+
+clsx@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
+ integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
+
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@@ -6048,7 +6159,7 @@ content-disposition@0.5.4, content-disposition@^0.5.4:
dependencies:
safe-buffer "5.2.1"
-content-type@~1.0.4:
+content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
@@ -6063,10 +6174,10 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
-cookie@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
- integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
+cookie@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
+ integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
cookie@^0.4.1:
version "0.4.2"
@@ -6251,10 +6362,10 @@ csstype@^3.0.2, csstype@^3.0.6:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
-csstype@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
- integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
+csstype@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+ integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
cypress@^12.17.4:
version "12.17.4"
@@ -7019,6 +7130,11 @@ dom-to-image@^2.6.0:
resolved "https://registry.yarnpkg.com/dom-to-image/-/dom-to-image-2.6.0.tgz#8a503608088c87b1c22f9034ae032e1898955867"
integrity sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==
+dompurify@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.0.tgz#8c6b9fe986969a33aa4686bd829cbe8e14dd9445"
+ integrity sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA==
+
dotenv@^16.3.1:
version "16.3.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
@@ -7050,15 +7166,10 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
-electron-to-chromium@^1.4.284:
- version "1.4.352"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.352.tgz#be96bd7c2f4b980deebc9338a49a67430a33ed73"
- integrity sha512-ikFUEyu5/q+wJpMOxWxTaEVk2M1qKqTGKKyfJmod1CPZxKfYnxVS41/GCBQg21ItBpZybyN8sNpRqCUGm+Zc4Q==
-
-electron-to-chromium@^1.4.431:
- version "1.4.450"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.450.tgz#df232c961ee9bf4e8980f86e96a6e9f291720138"
- integrity sha512-BLG5HxSELlrMx7dJ2s+8SFlsCtJp37Zpk2VAxyC6CZtbc+9AJeZHfYHbrlSgdXp6saQ8StMqOTEDaBKgA7u1sw==
+electron-to-chromium@^1.5.4:
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6"
+ integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==
emoji-regex@^8.0.0:
version "8.0.0"
@@ -7087,10 +7198,10 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
-enhanced-resolve@^5.10.0:
- version "5.12.0"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634"
- integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==
+enhanced-resolve@^5.17.1:
+ version "5.17.1"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
+ integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.2.0"
@@ -7166,10 +7277,10 @@ es-get-iterator@^1.1.2:
isarray "^2.0.5"
stop-iteration-iterator "^1.0.0"
-es-module-lexer@^0.9.0:
- version "0.9.3"
- resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
- integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
+es-module-lexer@^1.2.1:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78"
+ integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
es-shim-unscopables@^1.0.0:
version "1.0.0"
@@ -7188,12 +7299,13 @@ es-to-primitive@^1.2.1:
is-symbol "^1.0.2"
es5-ext@^0.10.12, es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.49, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@^0.10.62, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
- version "0.10.62"
- resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
- integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
+ version "0.10.64"
+ resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714"
+ integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==
dependencies:
es6-iterator "^2.0.3"
es6-symbol "^3.1.3"
+ esniff "^2.0.1"
next-tick "^1.1.0"
es6-error@^4.0.1:
@@ -7240,10 +7352,10 @@ es6-weak-map@^2.0.3:
es6-iterator "^2.0.3"
es6-symbol "^3.1.1"
-escalade@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
- integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+escalade@^3.1.2:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
escape-html@~1.0.3:
version "1.0.3"
@@ -7424,6 +7536,16 @@ esniff@^1.1.0:
d "1"
es5-ext "^0.10.12"
+esniff@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
+ integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
+ dependencies:
+ d "^1.0.1"
+ es5-ext "^0.10.62"
+ event-emitter "^0.3.5"
+ type "^2.7.2"
+
espree@^9.4.0:
version "9.4.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd"
@@ -7559,16 +7681,16 @@ expect@^29.0.0:
jest-util "^29.4.3"
express@^4.17.3:
- version "4.18.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
- integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
+ version "4.19.2"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
+ integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
- body-parser "1.20.1"
+ body-parser "1.20.2"
content-disposition "0.5.4"
content-type "~1.0.4"
- cookie "0.5.0"
+ cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
@@ -7927,9 +8049,9 @@ focus-lock@^0.11.6:
tslib "^2.0.3"
follow-redirects@^1.0.0, follow-redirects@^1.14.8:
- version "1.15.2"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
- integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
+ version "1.15.6"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
+ integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
for-each@^0.3.3:
version "0.3.3"
@@ -8273,12 +8395,7 @@ got@^11.8.5:
p-cancelable "^2.0.0"
responselike "^2.0.0"
-graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
- version "4.2.10"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
- integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
-
-graceful-fs@^4.1.15:
+graceful-fs@^4.1.10, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -10472,15 +10589,10 @@ node-preload@^0.2.1:
dependencies:
process-on-spawn "^1.0.0"
-node-releases@^2.0.12:
- version "2.0.12"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039"
- integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==
-
-node-releases@^2.0.8:
- version "2.0.10"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f"
- integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==
+node-releases@^2.0.18:
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
+ integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
@@ -10912,6 +11024,11 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+picocolors@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
+ integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
+
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -11495,10 +11612,10 @@ range-parser@^1.2.1, range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
-raw-body@2.5.1:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
- integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
+raw-body@2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
+ integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
@@ -12095,7 +12212,7 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
-reselect@^4.1.6, reselect@^4.1.8:
+reselect@^4.1.8:
version "4.1.8"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
@@ -12287,10 +12404,10 @@ schema-utils@^2.6.5, schema-utils@^2.7.0:
ajv "^6.12.4"
ajv-keywords "^3.5.2"
-schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
- integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
+schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
+ integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
dependencies:
"@types/json-schema" "^7.0.8"
ajv "^6.12.5"
@@ -12366,10 +12483,10 @@ send@0.18.0:
range-parser "~1.2.1"
statuses "2.0.1"
-serialize-javascript@^6.0.0:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c"
- integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==
+serialize-javascript@^6.0.1:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
+ integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
dependencies:
randombytes "^2.1.0"
@@ -12986,24 +13103,24 @@ tar-stream@^1.5.2:
to-buffer "^1.1.1"
xtend "^4.0.0"
-terser-webpack-plugin@^5.1.3:
- version "5.3.6"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c"
- integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==
+terser-webpack-plugin@^5.3.10:
+ version "5.3.10"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199"
+ integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==
dependencies:
- "@jridgewell/trace-mapping" "^0.3.14"
+ "@jridgewell/trace-mapping" "^0.3.20"
jest-worker "^27.4.5"
schema-utils "^3.1.1"
- serialize-javascript "^6.0.0"
- terser "^5.14.1"
+ serialize-javascript "^6.0.1"
+ terser "^5.26.0"
-terser@^5.14.1:
- version "5.16.3"
- resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.3.tgz#3266017a9b682edfe019b8ecddd2abaae7b39c6b"
- integrity sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==
+terser@^5.26.0:
+ version "5.31.6"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.6.tgz#c63858a0f0703988d0266a82fcbf2d7ba76422b1"
+ integrity sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==
dependencies:
- "@jridgewell/source-map" "^0.3.2"
- acorn "^8.5.0"
+ "@jridgewell/source-map" "^0.3.3"
+ acorn "^8.8.2"
commander "^2.20.0"
source-map-support "~0.5.20"
@@ -13433,21 +13550,13 @@ untildify@^4.0.0:
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
-update-browserslist-db@^1.0.10:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
- integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
- dependencies:
- escalade "^3.1.1"
- picocolors "^1.0.0"
-
-update-browserslist-db@^1.0.11:
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940"
- integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==
+update-browserslist-db@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e"
+ integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==
dependencies:
- escalade "^3.1.1"
- picocolors "^1.0.0"
+ escalade "^3.1.2"
+ picocolors "^1.0.1"
uri-js@^4.2.2:
version "4.4.1"
@@ -13456,6 +13565,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+urijs@^1.19.11:
+ version "1.19.11"
+ resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc"
+ integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==
+
url-parse@^1.5.3:
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
@@ -13567,10 +13681,10 @@ warning@^4.0.2:
dependencies:
loose-envify "^1.0.0"
-watchpack@^2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
- integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+watchpack@^2.4.1:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da"
+ integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==
dependencies:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
@@ -13613,9 +13727,9 @@ webpack-cli@^4.9.1:
webpack-merge "^5.7.3"
webpack-dev-middleware@^5.3.1:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f"
- integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==
+ version "5.3.4"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517"
+ integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==
dependencies:
colorette "^2.0.10"
memfs "^3.4.3"
@@ -13677,33 +13791,32 @@ webpack-virtual-modules@^0.5.0:
integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==
webpack@^5.77.0:
- version "5.77.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.77.0.tgz#dea3ad16d7ea6b84aa55fa42f4eac9f30e7eb9b4"
- integrity sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==
- dependencies:
- "@types/eslint-scope" "^3.7.3"
- "@types/estree" "^0.0.51"
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/wasm-edit" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
+ version "5.94.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f"
+ integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==
+ dependencies:
+ "@types/estree" "^1.0.5"
+ "@webassemblyjs/ast" "^1.12.1"
+ "@webassemblyjs/wasm-edit" "^1.12.1"
+ "@webassemblyjs/wasm-parser" "^1.12.1"
acorn "^8.7.1"
- acorn-import-assertions "^1.7.6"
- browserslist "^4.14.5"
+ acorn-import-attributes "^1.9.5"
+ browserslist "^4.21.10"
chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.10.0"
- es-module-lexer "^0.9.0"
+ enhanced-resolve "^5.17.1"
+ es-module-lexer "^1.2.1"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
- graceful-fs "^4.2.9"
+ graceful-fs "^4.2.11"
json-parse-even-better-errors "^2.3.1"
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"
- schema-utils "^3.1.0"
+ schema-utils "^3.2.0"
tapable "^2.1.1"
- terser-webpack-plugin "^5.1.3"
- watchpack "^2.4.0"
+ terser-webpack-plugin "^5.3.10"
+ watchpack "^2.4.1"
webpack-sources "^3.2.3"
websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
@@ -13825,9 +13938,9 @@ write-file-atomic@^4.0.2:
signal-exit "^3.0.7"
ws@^8.4.2:
- version "8.12.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f"
- integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==
+ version "8.17.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
+ integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
xtend@^4.0.0:
version "4.0.2"