From 7c7b723dffd658e41ccb97ea712dab752b2a4bf9 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 24 Jan 2024 12:34:48 +0100 Subject: [PATCH 01/54] Replace react-smooth-dnd with react-beautiful-dnd While react-smooth-dnd works perfectly fine, it is also unmaintained and will cause problems when updating to new major versions of react. This switches to a currently maintained library, which is a lot more boilerplate, but also designed for our use case (the list where you can edit which columns are displayed in a table and in what order). Also fixes a bug in the EditTable modal where changes made to the sorting (without saving) would revert themselves after a couple seconds. --- app/package-lock.json | 220 ++++++++++++++---- app/package.json | 2 +- .../components/shared/EditTableViewModal.tsx | 88 ++++--- 3 files changed, 239 insertions(+), 71 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 41caf91cf2..6b8f22b0db 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@date-io/date-fns": "^1.3.13", - "@edorivai/react-smooth-dnd": "^0.11.2", + "@hello-pangea/dnd": "^16.5.0", "@material-ui/core": "^4.12.4", "@material-ui/pickers": "^3.3.10", "@reduxjs/toolkit": "^1.9.7", @@ -1943,16 +1943,21 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/@babel/template": { "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", @@ -2290,18 +2295,6 @@ "date-fns": "^2.0.0" } }, - "node_modules/@edorivai/react-smooth-dnd": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/@edorivai/react-smooth-dnd/-/react-smooth-dnd-0.11.2.tgz", - "integrity": "sha512-PJeN2PoP4tR7faKdlvsTpKDtCHkmMd4RQzmXSnQ4fHrEcpeCdTuNtF0UNbQ0KlCFNoumaWpIptk6AqsYeUFq1Q==", - "dependencies": { - "prop-types": ">=15.6.0", - "smooth-dnd": "0.12.1" - }, - "peerDependencies": { - "react": ">=16.3" - } - }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", @@ -2558,6 +2551,62 @@ "@floating-ui/core": "^1.2.6" } }, + "node_modules/@hello-pangea/dnd": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.5.0.tgz", + "integrity": "sha512-n+am6O32jo/CFXciCysz83lPM3I3F58FJw4uS44TceieymcyxQSfzK5OhzPAKrVBZktmuOI6Zim9WABTMtXv4A==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "css-box-model": "^1.2.1", + "memoize-one": "^6.0.0", + "raf-schd": "^4.0.3", + "react-redux": "^8.1.3", + "redux": "^4.2.1", + "use-memo-one": "^1.1.3" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@hello-pangea/dnd/node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -5293,6 +5342,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/ws": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", @@ -7614,6 +7668,14 @@ "postcss": "^8.4" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -18514,6 +18576,11 @@ "performance-now": "^2.1.0" } }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -20140,11 +20207,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/smooth-dnd": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/smooth-dnd/-/smooth-dnd-0.12.1.tgz", - "integrity": "sha512-Dndj/MOG7VP83mvzfGCLGzV2HuK1lWachMtWl/Iuk6zV7noDycIBnflwaPuDzoaapEl3Pc4+ybJArkkx9sxPZg==" - }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -21333,6 +21395,11 @@ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", @@ -22584,6 +22651,22 @@ } } }, + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -24891,11 +24974,18 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } } }, "@babel/template": { @@ -25079,15 +25169,6 @@ "@date-io/core": "^1.3.13" } }, - "@edorivai/react-smooth-dnd": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/@edorivai/react-smooth-dnd/-/react-smooth-dnd-0.11.2.tgz", - "integrity": "sha512-PJeN2PoP4tR7faKdlvsTpKDtCHkmMd4RQzmXSnQ4fHrEcpeCdTuNtF0UNbQ0KlCFNoumaWpIptk6AqsYeUFq1Q==", - "requires": { - "prop-types": ">=15.6.0", - "smooth-dnd": "0.12.1" - } - }, "@emotion/babel-plugin": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", @@ -25298,6 +25379,35 @@ "@floating-ui/core": "^1.2.6" } }, + "@hello-pangea/dnd": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.5.0.tgz", + "integrity": "sha512-n+am6O32jo/CFXciCysz83lPM3I3F58FJw4uS44TceieymcyxQSfzK5OhzPAKrVBZktmuOI6Zim9WABTMtXv4A==", + "requires": { + "@babel/runtime": "^7.23.2", + "css-box-model": "^1.2.1", + "memoize-one": "^6.0.0", + "raf-schd": "^4.0.3", + "react-redux": "^8.1.3", + "redux": "^4.2.1", + "use-memo-one": "^1.1.3" + }, + "dependencies": { + "react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + } + } + } + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -27364,6 +27474,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "@types/ws": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", @@ -29095,6 +29210,14 @@ "postcss-selector-parser": "^6.0.9" } }, + "css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -36981,6 +37104,11 @@ "performance-now": "^2.1.0" } }, + "raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -38171,11 +38299,6 @@ } } }, - "smooth-dnd": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/smooth-dnd/-/smooth-dnd-0.12.1.tgz", - "integrity": "sha512-Dndj/MOG7VP83mvzfGCLGzV2HuK1lWachMtWl/Iuk6zV7noDycIBnflwaPuDzoaapEl3Pc4+ybJArkkx9sxPZg==" - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -39097,6 +39220,11 @@ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", @@ -40053,6 +40181,18 @@ "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", "requires": {} }, + "use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "requires": {} + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/app/package.json b/app/package.json index 54078f614e..3fef7d7820 100644 --- a/app/package.json +++ b/app/package.json @@ -5,7 +5,7 @@ "homepage": "/admin-ui", "dependencies": { "@date-io/date-fns": "^1.3.13", - "@edorivai/react-smooth-dnd": "^0.11.2", + "@hello-pangea/dnd": "^16.5.0", "@material-ui/core": "^4.12.4", "@material-ui/pickers": "^3.3.10", "@reduxjs/toolkit": "^1.9.7", diff --git a/app/src/components/shared/EditTableViewModal.tsx b/app/src/components/shared/EditTableViewModal.tsx index 9d3a1cb782..2a88a34b5f 100644 --- a/app/src/components/shared/EditTableViewModal.tsx +++ b/app/src/components/shared/EditTableViewModal.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect } from "react"; -import { Container, Draggable } from "@edorivai/react-smooth-dnd"; import { arrayMoveImmutable } from "array-move"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -9,6 +8,7 @@ import { getDeactivatedColumns, getResourceType, } from "../../selectors/tableSelectors"; +import { DragDropContext, Droppable, OnDragEndResponder, Draggable as Draggablee } from "@hello-pangea/dnd"; /** * This component renders the modal for editing which columns are shown in the table @@ -34,11 +34,17 @@ const EditTableViewModal = ({ const [deactivatedCols, setDeactivatedColumns] = useState(deactivatedColumns); const [activeCols, setActiveColumns] = useState(activeColumns); + const [isColsLoaded, setIsColsLoaded] = useState(false); useEffect(() => { - setActiveColumns(activeColumns); - setDeactivatedColumns(deactivatedColumns); - }, [activeColumns, deactivatedColumns]); + if (!isColsLoaded) { + setActiveColumns(activeColumns); + setDeactivatedColumns(deactivatedColumns); + if (activeColumns.length !== 0 || deactivatedColumns.length !== 0) { + setIsColsLoaded(true) + } + } + }, [activeColumns, deactivatedColumns, isColsLoaded]); // closes this modal const close = () => { @@ -76,11 +82,15 @@ const EditTableViewModal = ({ }; // change column order based on where column was dragged and dropped -// @ts-expect-error TS(7031): Binding element 'removedIndex' implicitly has an '... Remove this comment to see the full error message - const onDrop = ({ removedIndex, addedIndex }) => { -// @ts-expect-error TS(7006): Parameter 'columns' implicitly has an 'any' type. - setActiveColumns((columns) => arrayMoveImmutable(columns, removedIndex, addedIndex)); - }; + const onDragEnd: OnDragEndResponder = (result) => { + // dropped outside the list + if (!result.destination) { + return; + } + + // @ts-expect-error TS(7006): Parameter 'columns' implicitly has an 'any' type. + setActiveColumns((columns) => arrayMoveImmutable(columns, result.source.index, result.destination.index)); + } return ( <> @@ -156,28 +166,46 @@ const EditTableViewModal = ({ From 3af97a11dcba5cdafa0eda8a4db7cebe2bb2f970 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 1 Feb 2024 15:50:45 +0100 Subject: [PATCH 02/54] Avoid crashing by not throwing error Potentially controversial fix intended to keep nodemon from crashing. Instead of having the proxy server throw certain kinds of errors, this changes it to merely logging them. The throwing in and of itself is likely not the root cause of the nodemon crashing issue, but removing it seems to alleviate the issue nonetheless. I'm not certain on what we should ideally do with the kinds of errors that were previously thrown in the first place though. --- proxyServer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxyServer.js b/proxyServer.js index 168113770f..5a5bde6ffb 100644 --- a/proxyServer.js +++ b/proxyServer.js @@ -68,7 +68,8 @@ app.use('', (req, res, next) => { let onReadFromBackend = function (error, response, body) { if (error && (typeof error != 'object' || !error.hasOwnProperty('statusCode') || !error.hasOwnProperty('body'))) { - throw error; + console.error(error); + return; } // forward to client From 97aebe18e5ceed92761e2730836637fa16a2f821 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 6 Feb 2024 15:43:51 +0100 Subject: [PATCH 03/54] Add notes column back in Add a column called "notes" to the event table. This was already present in the old admin ui. The column provides a small text area where a user can take short on an event. The notes are saved as a comment (and can thus also be viewed in the event details). The notes column is disabled by default, and needs to be manually enabled. Only if it is enabled will additional comment information be fetched from the backend, which puts a bit of additional load on the Opencast admin if you have a lot of comments. --- app/src/actions/eventDetailsActions.ts | 10 ++ .../events/partials/EventsNotesCell.tsx | 106 ++++++++++++++++++ .../configs/tableConfigs/eventsTableConfig.ts | 9 ++ .../adminui/languages/lang-de_DE.json | 4 +- .../adminui/languages/lang-en_US.json | 4 +- app/src/reducers/eventReducers.ts | 2 +- app/src/styles/components/_tables.scss | 29 +++++ app/src/thunks/eventDetailsThunks.ts | 32 ++++++ app/src/thunks/eventThunks.ts | 11 +- .../GET/admin-ng/resources/components.json | 3 +- 10 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 app/src/components/events/partials/EventsNotesCell.tsx diff --git a/app/src/actions/eventDetailsActions.ts b/app/src/actions/eventDetailsActions.ts index 4e106f9bb9..4b747582a3 100644 --- a/app/src/actions/eventDetailsActions.ts +++ b/app/src/actions/eventDetailsActions.ts @@ -60,6 +60,8 @@ export const SAVE_COMMENT_DONE = "SAVE_COMMENT_DONE"; export const SAVE_COMMENT_IN_PROGRESS = "SAVE_COMMENT_IN_PROGRESS"; export const SAVE_COMMENT_REPLY_DONE = "SAVE_COMMENT_REPLY_DONE"; export const SAVE_COMMENT_REPLY_IN_PROGRESS = "SAVE_COMMENT_REPLY_IN_PROGRESS"; +export const UPDATE_COMMENT_DONE = "UPDATE_COMMENT_DONE"; +export const UPDATE_COMMENT_IN_PROGRESS = "UPDATE_COMMENT_IN_PROGRESS"; // Constants of actions types affecting the publications tab export const LOAD_EVENT_PUBLICATIONS_IN_PROGRESS = @@ -350,6 +352,14 @@ export const saveCommentReplyDone = () => ({ type: SAVE_COMMENT_REPLY_DONE, }); +export const updateCommentInProgress = () => ({ + type: UPDATE_COMMENT_IN_PROGRESS, +}); + +export const updateCommentDone = () => ({ + type: UPDATE_COMMENT_DONE, +}); + // actions for publications export const loadEventPublicationsInProgress = () => ({ type: LOAD_EVENT_PUBLICATIONS_IN_PROGRESS, diff --git a/app/src/components/events/partials/EventsNotesCell.tsx b/app/src/components/events/partials/EventsNotesCell.tsx new file mode 100644 index 0000000000..a004b42933 --- /dev/null +++ b/app/src/components/events/partials/EventsNotesCell.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { connect } from "react-redux"; +import { deleteComment, saveComment, updateComment } from "../../../thunks/eventDetailsThunks"; +import { updatePages } from "../../../thunks/tableThunks"; + +/** + * This component renders the location cells of events in the table view + */ +const EventsNotesCell = ({ +// @ts-expect-error TS(7031): Binding element 'row' implicitly has an 'any' type... Remove this comment to see the full error message + row, + // @ts-expect-error TS(7031): + saveNewComment, + // @ts-expect-error TS(7031): + updateNewComment, + // @ts-expect-error TS(7031): + deleteOneComment, + // @ts-expect-error TS(7031): + updatePages, +}) => { + const notesCommentReason = 'EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.ADMINUI_NOTES'; + + // Return early if comments are not loaded yet + if (!row.comments) { + return <>; + } + + // @ts-expect-error TS(7031): + const comments = row.comments.filter((comment) => comment.reason === notesCommentReason) + + const createComment = (event: React.FocusEvent) => { + if (!event.target.value || !row.id) { + return; + } + saveNewComment(row.id, event.target.value, notesCommentReason) + .then(() => { + updatePages(); + }); + } + + const updateComment = (event: React.ChangeEvent, commentId: any) => { + if (!event.target.value || !row.id || !commentId) { + return; + } + updateNewComment(row.id, commentId, event.target.value, notesCommentReason) + } + + const deleteComment = (event: React.FocusEvent, commentId: any) => { + if (!row.id || !commentId) { + return; + } + if (event.target.value === "") { + deleteOneComment(row.id, commentId) + .then(() => { + updatePages(); + }); + } + } + + return ( +
+ {comments.length === 0 && + + } + {/* @ts-expect-error TS(7031): */} + {comments.map((comment, key) => ( +
+
+ +
+ ))} +
+ ); +}; + +// Getting state data out of redux store +// @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. +const mapStateToProps = (state) => ({ + // comments: getComments(state), +}); + +// Mapping actions to dispatch +// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. +const mapDispatchToProps = (dispatch) => ({ + // @ts-expect-error TS(7006): Parameter 'eventId' implicitly has an 'any' type. + saveNewComment: (eventId, commentText, commentReason) => + dispatch(saveComment(eventId, commentText, commentReason)), + // @ts-expect-error TS(7006): Parameter 'eventId' implicitly has an 'any' type. + updateNewComment: (eventId, commentId, commentText, commentReason) => + dispatch(updateComment(eventId, commentId, commentText, commentReason)), + // @ts-expect-error TS(7006): Parameter 'eventId' implicitly has an 'any' type. + deleteOneComment: (eventId, commentId) => + dispatch(deleteComment(eventId, commentId)), + updatePages: () => dispatch(updatePages()), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(EventsNotesCell); diff --git a/app/src/configs/tableConfigs/eventsTableConfig.ts b/app/src/configs/tableConfigs/eventsTableConfig.ts index 7e1f99cd1d..4466e0c4c8 100644 --- a/app/src/configs/tableConfigs/eventsTableConfig.ts +++ b/app/src/configs/tableConfigs/eventsTableConfig.ts @@ -8,6 +8,7 @@ import PublishedCell from "../../components/events/partials/PublishedCell"; import EventsLocationCell from "../../components/events/partials/EventsLocationCell"; import EventsEndCell from "../../components/events/partials/EventsEndCell"; import EventsStartCell from "../../components/events/partials/EventsStartCell"; +import EventsNotesCell from "../../components/events/partials/EventsNotesCell"; /** * Config that contains the columns and further information regarding events. These are the information that never or hardly changes. @@ -88,6 +89,13 @@ export const eventsTableConfig = { label: "EVENTS.EVENTS.TABLE.ACTION", translate: false, }, + { + name: "notes", + template: "EventsNotesCell", + label: "EVENTS.EVENTS.TABLE.ADMINUI_NOTES", + translate: false, + deactivated: true, + }, ], caption: "EVENTS.EVENTS.TABLE.CAPTION", resource: "events", @@ -110,4 +118,5 @@ export const eventsTemplateMap = { EventsStatusCell: EventsStatusCell, EventsTechnicalDateCell: EventsTechnicalDateCell, PublishedCell: PublishedCell, + EventsNotesCell: EventsNotesCell, }; diff --git a/app/src/i18n/org/opencastproject/adminui/languages/lang-de_DE.json b/app/src/i18n/org/opencastproject/adminui/languages/lang-de_DE.json index 72904d4a99..5ab0c05af2 100644 --- a/app/src/i18n/org/opencastproject/adminui/languages/lang-de_DE.json +++ b/app/src/i18n/org/opencastproject/adminui/languages/lang-de_DE.json @@ -587,6 +587,7 @@ "NOCONTENT": "Keine Daten verfügbar", "PUBLISHED": "Veröffentlicht", "WEEKDAY": "Wochentag", + "ADMINUI_NOTES": "Notizen", "TOOLTIP": { "ASSETS": "Anlagedetails öffnen", "START": "Nach diesem Startdatum filtern", @@ -875,7 +876,8 @@ "WRONG_METADATA": "Metadaten benötigen Korrektur", "WRONG_SERIES_PUBLICATION": "Falsche Serie oder falscher Publikations-Kanal", "WRONG_WORKFLOW": "Falscher Workflow", - "PROCESSING_FAILURE": "Verarbeitungs-Fehler" + "PROCESSING_FAILURE": "Verarbeitungs-Fehler", + "ADMINUI_NOTES": "Notizen in Admin UI" } }, "STATISTICS": { diff --git a/app/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json b/app/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json index 72440e73b9..d5982bb5e4 100644 --- a/app/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json +++ b/app/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json @@ -591,6 +591,7 @@ "NOCONTENT": "No data available", "PUBLISHED": "Published", "WEEKDAY": "Weekday", + "ADMINUI_NOTES": "Notes", "TOOLTIP": { "ASSETS": "Open asset details", "START": "Filter for this start date", @@ -879,7 +880,8 @@ "WRONG_METADATA": "Metadata needs correction", "WRONG_SERIES_PUBLICATION": "Wrong series or publication channel", "WRONG_WORKFLOW": "Wrong workflow", - "PROCESSING_FAILURE": "Processing failure" + "PROCESSING_FAILURE": "Processing failure", + "ADMINUI_NOTES": "Notes in Admin UI" } }, "STATISTICS": { diff --git a/app/src/reducers/eventReducers.ts b/app/src/reducers/eventReducers.ts index a18a66f1a5..51fe3a1f07 100644 --- a/app/src/reducers/eventReducers.ts +++ b/app/src/reducers/eventReducers.ts @@ -26,8 +26,8 @@ import { // Fill columns initially with columns defined in eventsTableConfig const initialColumns = eventsTableConfig.columns.map((column) => ({ - ...column, deactivated: false, + ...column, })); // Initial state of events in redux store diff --git a/app/src/styles/components/_tables.scss b/app/src/styles/components/_tables.scss index ea866eef41..559f23cb8c 100644 --- a/app/src/styles/components/_tables.scss +++ b/app/src/styles/components/_tables.scss @@ -310,6 +310,35 @@ } } + .comment-container { + overflow-y: auto; + + .comment { + &:first-child h4 { + margin-top: 0; + } + + // Hide first HR + &:first-child > hr { + display: none; + } + + > hr { + background: lighten($main-border-color, 10%); + height: 1px; + border: none; + } + } + + .textarea { + font-size: 11px; + line-height: 15px; + width: 100%; + resize: none; + padding: 5px 10px; + } + } + &.nowrap { white-space: nowrap; } diff --git a/app/src/thunks/eventDetailsThunks.ts b/app/src/thunks/eventDetailsThunks.ts index ac3239cb39..d048799913 100644 --- a/app/src/thunks/eventDetailsThunks.ts +++ b/app/src/thunks/eventDetailsThunks.ts @@ -78,6 +78,8 @@ import { loadEventStatisticsFailure, updateEventStatisticsSuccess, updateEventStatisticsFailure, + updateCommentInProgress, + updateCommentDone, } from "../actions/eventDetailsActions"; import { removeNotificationWizardForm } from "../actions/notificationActions"; import { addNotification } from "./notificationThunks"; @@ -746,6 +748,36 @@ export const saveComment = (eventId, commentText, commentReason) => async ( } }; +// @ts-expect-error TS(7006): Parameter 'eventId' implicitly has an 'any' type. +export const updateComment = (eventId, commentId, commentText, commentReason) => async ( +// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. + dispatch +) => { + try { + dispatch(updateCommentInProgress()); + + let headers = getHttpHeaders(); + + let data = new URLSearchParams(); + data.append("text", commentText); + data.append("reason", commentReason); + + const commentUpdated = await axios.post( + `/admin-ng/event/${eventId}/comment/${commentId}`, + data.toString(), + headers + ); + await commentUpdated.data; + + dispatch(updateCommentDone()); + return true; + } catch (e) { + dispatch(updateCommentDone()); + console.error(e); + return false; + } +}; + // @ts-expect-error TS(7006): Parameter 'eventId' implicitly has an 'any' type. export const deleteComment = (eventId, commentId) => async () => { try { diff --git a/app/src/thunks/eventThunks.ts b/app/src/thunks/eventThunks.ts index e9623e2e72..cd6b4cdac2 100644 --- a/app/src/thunks/eventThunks.ts +++ b/app/src/thunks/eventThunks.ts @@ -40,7 +40,16 @@ export const fetchEvents = () => async (dispatch, getState) => { dispatch(loadEventsInProgress()); const state = getState(); - let params = getURLParams(state); + let params: any = getURLParams(state); + + // Only if the notes column is enabled, fetch comment information for events + // @ts-expect-error TS(7006): + if (state.table.columns.find(column => column.label === "EVENTS.EVENTS.TABLE.ADMINUI_NOTES" && !column.deactivated)) { + params = { + ...params, + getComments: true + } + } //admin-ng/event/events.json?filter={filter}&sort={sort}&limit=0&offset=0 let data = await axios.get("/admin-ng/event/events.json", { diff --git a/test/app/GET/admin-ng/resources/components.json b/test/app/GET/admin-ng/resources/components.json index a3ca90cb48..0bbab7481d 100644 --- a/test/app/GET/admin-ng/resources/components.json +++ b/test/app/GET/admin-ng/resources/components.json @@ -17,7 +17,8 @@ "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.UNKNOWN_CREATOR": "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.UNKNOWN_CREATOR", "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.WRONG_METADATA": "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.WRONG_METADATA", "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.WRONG_WORKFLOW": "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.WRONG_WORKFLOW", - "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.MISSING_AGREEMENT": "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.MISSING_AGREEMENT" + "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.MISSING_AGREEMENT": "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.MISSING_AGREEMENT", + "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.ADMINUI_NOTES": "EVENTS.EVENTS.DETAILS.COMMENTS.REASONS.ADMINUI_NOTES" }, "workflowsOperations": { "retract": "EVENTS.EVENTS.DETAILS.PUBLICATIONS.RETRACT", From ccb73fca12dc7385feb0d71eb7759f5849e1fdca Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 7 Feb 2024 14:26:55 +0100 Subject: [PATCH 04/54] Refetch event table immediately after changing columns Adds a fetch event when changing the column selection for the event table. This is purely to fill the notes column with data immediately after it is selected. --- app/src/thunks/tableThunks.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/thunks/tableThunks.ts b/app/src/thunks/tableThunks.ts index 7e09e6da6b..3e7ba980f0 100644 --- a/app/src/thunks/tableThunks.ts +++ b/app/src/thunks/tableThunks.ts @@ -600,6 +600,9 @@ export const changeColumnSelection = (updatedColumns) => async ( dispatch(loadEventsIntoTable()); + await dispatch(fetchEvents()); + dispatch(loadEventsIntoTable()); + break; } case "series": { From 00af3ca454bbd13abcd52e97aee7970b639f1639 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 8 Feb 2024 14:39:25 +0100 Subject: [PATCH 05/54] Respect "displayFallback" The admin ui documentation for "Asset Upload" specifies both "displayOverride" and "displayFallback" as ways to replace translations. We were only respecting "displayOverride" and completely ignoring "displayFallback". This changes that. To test this, you should have access to an Opencast instance where you can modify listproviders/event.upload.asset.options.properties. Then add something like the following line to that file EVENTS.EVENTS.NEW.SOURCE.UPLOAD.NON_SEGMENTABLE_PART0={"id": "track_presenter_part0","type":"track", "flavorType": "presenter","flavorSubType": "source+part-0", "multiple":false, "displayOrder":20, "accept": ".avi,.flv,.m4v,.mkv,.mov,.mp4,.mpeg,.mpg,.ogv,.webm,.wmv", "displayFallback.SHORT": "Multipart: Presenter Part 1", "displayFallback.DETAIL": "This option should be chosen, if you want to concatenate multiple presenter videos."} --- .../EventDetailsAssetsAddAsset.tsx | 5 ++- .../ModalTabsAndPages/NewAssetUploadPage.tsx | 5 ++- .../ModalTabsAndPages/NewSourcePage.tsx | 11 ++----- .../partials/wizards/NewEventSummary.tsx | 10 ++---- app/src/utils/utils.ts | 32 +++++++++++++++++++ 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/app/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsAddAsset.tsx b/app/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsAddAsset.tsx index 65e7776f93..4528231b67 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsAddAsset.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsAddAsset.tsx @@ -6,6 +6,7 @@ import { style_button_spacing } from "../../../../utils/eventDetailsUtils"; import { Formik } from "formik"; import { updateAssets } from "../../../../thunks/eventDetailsThunks"; import { getAssetUploadOptions } from "../../../../selectors/eventSelectors"; +import { translateOverrideFallback } from "../../../../utils/utils"; /** * This component manages the add asset sub-tab for assets tab of event details modal @@ -89,9 +90,7 @@ const EventDetailsAssetsAddAsset = ({ {" "} - {!!asset.displayOverride - ? t(asset.displayOverride) - : t(asset.title)} + {translateOverrideFallback(asset, t)} { // eslint-disable-next-line react/jsx-no-comment-textnodes } ({asset.type} "{asset.flavorType}// diff --git a/app/src/components/events/partials/ModalTabsAndPages/NewAssetUploadPage.tsx b/app/src/components/events/partials/ModalTabsAndPages/NewAssetUploadPage.tsx index 93f02877ad..ec110e29c2 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/NewAssetUploadPage.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/NewAssetUploadPage.tsx @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next"; import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; import { connect } from "react-redux"; import { getAssetUploadOptions } from "../../../../selectors/eventSelectors"; +import { translateOverrideFallback } from "../../../../utils/utils"; /** * This component renders the asset upload page of the new event wizard @@ -62,9 +63,7 @@ const NewAssetUploadPage = ({ {" "} - {!!asset.displayOverride - ? t(asset.displayOverride) - : t(asset.title)} + {translateOverrideFallback(asset, t)} { // eslint-disable-next-line react/jsx-no-comment-textnodes } ({asset.type} "{asset.flavorType}// diff --git a/app/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx b/app/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx index 5ce4a0c671..93d7eca95c 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx @@ -6,6 +6,7 @@ import { MuiPickersUtilsProvider, DatePicker } from "@material-ui/pickers"; import { getCurrentLanguageInformation, getTimezoneOffset, + translateOverrideFallback, } from "../../../../utils/utils"; import { createMuiTheme, ThemeProvider } from "@material-ui/core"; import { Field, FieldArray } from "formik"; @@ -264,20 +265,14 @@ const Upload = ({ formik }) => { - {t( - asset.title + ".SHORT", - asset["displayOverride.SHORT"] - )} + {translateOverrideFallback(asset, t, "SHORT")} ({asset.type} "{asset.flavorType}/ {asset.flavorSubType}")

- {t( - asset.title + ".DETAIL", - asset["displayOverride.DETAIL"] - )} + {translateOverrideFallback(asset, t, "DETAIL")}

diff --git a/app/src/components/events/partials/wizards/NewEventSummary.tsx b/app/src/components/events/partials/wizards/NewEventSummary.tsx index e0b95b96d0..3c0c15571f 100644 --- a/app/src/components/events/partials/wizards/NewEventSummary.tsx +++ b/app/src/components/events/partials/wizards/NewEventSummary.tsx @@ -11,6 +11,7 @@ import MetadataSummaryTable from "./summaryTables/MetadataSummaryTable"; import MetadataExtendedSummaryTable from "./summaryTables/MetadataExtendedSummaryTable"; import AccessSummaryTable from "./summaryTables/AccessSummaryTable"; import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; +import { translateOverrideFallback } from "../../../../utils/utils"; /** * This component renders the summary page for new events in the new event wizard. @@ -50,9 +51,7 @@ const NewEventSummary = ({ // @ts-expect-error TS(7005): Variable 'uploadAssetsNonTrack' implicitly has an ... Remove this comment to see the full error message uploadAssetsNonTrack = uploadAssetsNonTrack.concat({ name: uploadAssetsOptionsNonTrack[i].id, - translate: !!uploadAssetsOptionsNonTrack[i].displayOverride - ? t(uploadAssetsOptionsNonTrack[i].displayOverride) - : t(uploadAssetsOptionsNonTrack[i].title), + translate: translateOverrideFallback(uploadAssetsOptionsNonTrack[i], t), type: uploadAssetsOptionsNonTrack[i].type, flavorType: uploadAssetsOptionsNonTrack[i].flavorType, flavorSubType: uploadAssetsOptionsNonTrack[i].flavorSubType, @@ -138,10 +137,7 @@ const NewEventSummary = ({ !!asset.file ? ( - {t( - asset.title + ".SHORT", - asset["displayOverride.SHORT"] - )} + {translateOverrideFallback(asset, t, "SHORT")} { // eslint-disable-next-line react/jsx-no-comment-textnodes } ({asset.type} "{asset.flavorType}// diff --git a/app/src/utils/utils.ts b/app/src/utils/utils.ts index e308f3c433..6f2a388d51 100644 --- a/app/src/utils/utils.ts +++ b/app/src/utils/utils.ts @@ -1,5 +1,6 @@ import languages from "../i18n/languages"; import i18n from "../i18n/i18n"; +import { TFunction } from "i18next"; /** * This File contains methods that are needed in more than one places @@ -145,3 +146,34 @@ export const isJson = (text) => { return false; } }; + +/** + * Handles translation for assets with overrides or fallbackes in their + * translation as defined in Opencast documentation under + * /docs/guides/admin/docs/configuration/admin-ui/asset-upload.md + * + * Asset is expected to have a field "title" that contains a translation string + * t is the hook returned by i18next.useTranslation + * suffix further specifices the asset value if necessary, e.g. "SHORT" for "displayOverride.SHORT" + */ +export const translateOverrideFallback = (asset: any, t: TFunction, suffix?: string) => { + let result = undefined; + const sub = !!suffix ? "." + suffix : ""; + const translatable = asset["title"] + sub; + + if (asset['displayOverride' + sub]) { + result = asset['displayOverride' + sub]; + + } else if (i18n.exists(translatable)) { + result = t(translatable); + + } else if (asset['displayFallback' + sub]) { + result = asset['displayFallback' + sub]; + + } else { + // no translate, override, or fallback, use what is given + result = translatable; + } + + return result; +} From ba324894b05899c10809bae03b28e8ca32292604 Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 12 Feb 2024 10:09:32 +0100 Subject: [PATCH 06/54] Reapply "Fix default routing" This reverts commit 3609c28ff97792d62466650126bfc7e4da544170. --- app/src/App.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/App.tsx b/app/src/App.tsx index f7b652662a..23b69437b8 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -55,8 +55,7 @@ function App({ Element; }' is... Remove this comment to see the full error message - render={() => } + element={} /> From 10da02f96b917c6c0e670064a2543f0505eaaf17 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 5 Mar 2024 15:10:25 +0100 Subject: [PATCH 07/54] Sort asset upload options by displayOrder User can specify a display order in the event upload asset listprovider, but we do not honor it. With this change we honor it. --- app/src/components/events/partials/wizards/NewEventWizard.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/components/events/partials/wizards/NewEventWizard.tsx b/app/src/components/events/partials/wizards/NewEventWizard.tsx index 830adc6665..77b7824301 100644 --- a/app/src/components/events/partials/wizards/NewEventWizard.tsx +++ b/app/src/components/events/partials/wizards/NewEventWizard.tsx @@ -273,6 +273,8 @@ const getInitialValues = ( if (!!uploadAssetOptions) { // @ts-expect-error TS(2339): Property 'uploadAssetsTrack' does not exist on typ... Remove this comment to see the full error message initialValues.uploadAssetsTrack = []; + // Sort by displayOrder + uploadAssetOptions = uploadAssetOptions.slice().sort((a: any, b: any) => a.displayOrder - b.displayOrder) // initial value of upload asset needs to be null, because object (file) is saved there // @ts-expect-error TS(7006): Parameter 'option' implicitly has an 'any' type. uploadAssetOptions.forEach((option) => { From 6aa23b07cd71b5b80ae78f62c91b8f9d2563c5b3 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 6 Mar 2024 11:37:04 +0100 Subject: [PATCH 08/54] Modernize redux: tableFilterProfilesSlice Helps with #213. Switching to redux toolkit for handling table filter profiles (you can save your table filters into various profiles). --- app/src/actions/tableFilterProfilesActions.ts | 32 -------- .../components/shared/TableFilterProfiles.tsx | 44 ++++------- .../reducers/tableFilterProfilesReducer.ts | 60 --------------- .../selectors/tableFilterProfilesSelectors.ts | 3 +- app/src/slices/tableFilterProfilesSlice.ts | 76 +++++++++++++++++++ app/src/store.ts | 2 +- 6 files changed, 93 insertions(+), 124 deletions(-) delete mode 100644 app/src/actions/tableFilterProfilesActions.ts delete mode 100644 app/src/reducers/tableFilterProfilesReducer.ts create mode 100644 app/src/slices/tableFilterProfilesSlice.ts diff --git a/app/src/actions/tableFilterProfilesActions.ts b/app/src/actions/tableFilterProfilesActions.ts deleted file mode 100644 index bd47558861..0000000000 --- a/app/src/actions/tableFilterProfilesActions.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * This file contains all redux actions that can be executed on table filter profiles - */ - -// Constants of action types concerning filter profile -export const CREATE_FILTER_PROFILE = "CREATE_FILTER_PROFILE"; -export const EDIT_FILTER_PROFILE = "EDIT_FILTER_PROFILE"; -export const REMOVE_FILTER_PROFILE = "REMOVE_FILTER_PROFILE"; -export const CANCEL_EDITING_FILTER_PROFILE = "CANCEL_EDITING_FILTER_PROFILE"; - -// Actions affecting filter profiles - -export const createFilterProfile = (filterProfile: any) => ({ - type: CREATE_FILTER_PROFILE, - payload: { filterProfile } -}); - -// @ts-expect-error TS(7006): Parameter 'filterProfile' implicitly has an 'any' ... Remove this comment to see the full error message -export const editFilterProfile = (filterProfile) => ({ - type: EDIT_FILTER_PROFILE, - payload: { filterProfile }, -}); - -// @ts-expect-error TS(7006): Parameter 'filterProfile' implicitly has an 'any' ... Remove this comment to see the full error message -export const removeFilterProfile = (filterProfile) => ({ - type: REMOVE_FILTER_PROFILE, - payload: { filterProfile }, -}); - -export const cancelEditFilterProfile = () => ({ - type: CANCEL_EDITING_FILTER_PROFILE, -}); diff --git a/app/src/components/shared/TableFilterProfiles.tsx b/app/src/components/shared/TableFilterProfiles.tsx index 3e76ddeded..069b1737a3 100644 --- a/app/src/components/shared/TableFilterProfiles.tsx +++ b/app/src/components/shared/TableFilterProfiles.tsx @@ -8,9 +8,10 @@ import { createFilterProfile, editFilterProfile, removeFilterProfile, -} from "../../actions/tableFilterProfilesActions"; +} from "../../slices/tableFilterProfilesSlice"; import { getFilters } from "../../selectors/tableFilterSelectors"; import { loadFilterProfile } from "../../actions/tableFilterActions"; +import { useAppDispatch, useAppSelector } from "../../store"; /** * This component renders the table filter profiles in the upper right corner when clicked on settings icon of the @@ -21,16 +22,8 @@ const TableFiltersProfiles = ({ showFilterSettings, // @ts-expect-error TS(7031): Binding element 'setFilterSettings' implicitly has... Remove this comment to see the full error message setFilterSettings, -// @ts-expect-error TS(7031): Binding element 'createFilterProfile' implicitly h... Remove this comment to see the full error message - createFilterProfile, // @ts-expect-error TS(7031): Binding element 'filterMap' implicitly has an 'any... Remove this comment to see the full error message filterMap, -// @ts-expect-error TS(7031): Binding element 'cancelEditFilterProfile' implicit... Remove this comment to see the full error message - cancelEditFilterProfile, -// @ts-expect-error TS(7031): Binding element 'profiles' implicitly has an 'any'... Remove this comment to see the full error message - profiles, -// @ts-expect-error TS(7031): Binding element 'removeFilterProfile' implicitly h... Remove this comment to see the full error message - removeFilterProfile, // @ts-expect-error TS(7031): Binding element 'loadFilterProfile' implicitly has... Remove this comment to see the full error message loadFilterProfile, // @ts-expect-error TS(7031): Binding element 'loadResource' implicitly has an '... Remove this comment to see the full error message @@ -40,6 +33,10 @@ const TableFiltersProfiles = ({ // @ts-expect-error TS(7031): Binding element 'resource' implicitly has an 'any'... Remove this comment to see the full error message resource, }) => { + const dispatch = useAppDispatch(); + + const profiles = useAppSelector(state => getFilterProfiles(state)); + // State for switching between list of profiles and saving/editing dialog const [settingsMode, setSettingsMode] = useState(true); @@ -52,7 +49,6 @@ const TableFiltersProfiles = ({ const { t } = useTranslation(); const currentProfiles = profiles.filter( -// @ts-expect-error TS(7006): Parameter 'profile' implicitly has an 'any' type. (profile) => profile.resource === resource ); @@ -65,7 +61,7 @@ const TableFiltersProfiles = ({ filterMap: filterMap, resource: resource, }; - createFilterProfile(filterProfile); + dispatch(createFilterProfile(filterProfile)); } setSettingsMode(!settingsMode); resetStateValues(); @@ -77,15 +73,16 @@ const TableFiltersProfiles = ({ setCurrentlyEditing(profile); setProfileName(profile.name); setProfileDescription(profile.description); - removeFilterProfile(profile); + dispatch(removeFilterProfile(profile)); setValidName(true); }; const cancelEditProfile = () => { - if (currentlyEditing !== "") { - createFilterProfile(currentlyEditing); - } - cancelEditFilterProfile(); + // What was this achieving? + // if (currentlyEditing !== "") { + // dispatch(createFilterProfile(currentlyEditing)); + // } + dispatch(cancelEditFilterProfile()); setSettingsMode(!settingsMode); setFilterSettings(!showFilterSettings); resetStateValues(); @@ -105,7 +102,6 @@ const TableFiltersProfiles = ({ if (itemName === "name") { const isDuplicated = profiles.some( -// @ts-expect-error TS(7006): Parameter 'profile' implicitly has an 'any' type. (profile) => profile.name === itemValue ); if (!isDuplicated) { @@ -151,7 +147,6 @@ const TableFiltersProfiles = ({
  • {t("TABLE_FILTERS.PROFILES.EMPTY")}
  • ) : ( // repeat for each profile in profiles filtered for currently shown resource (else-case) -// @ts-expect-error TS(7006): Parameter 'profile' implicitly has an 'any' type. currentProfiles.map((profile, key) => (