diff --git a/Dockerfile b/Dockerfile index 88dd4bd31..26fff6d8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM gplane/pnpm:8.6.0-node16-alpine as build-env -RUN apk add tzdata +RUN apk add tzdata openssl ENV TZ=Europe/Prague WORKDIR /app @@ -12,8 +12,11 @@ COPY ./packages . RUN cd client && pnpm install && BUILD_TIMESTAMP=$(date +'%a %d.%m.%Y %H:%M') pnpm build:${ENV} RUN rm -rf client/node_modules client/src -RUN cd server && pnpm install && pnpm build -RUN cd server && pnpm prune --prod +WORKDIR /app/server +RUN pnpm install && pnpm build +RUN pnpm prune --prod +RUN mkdir -p ./secret +RUN openssl req -x509 -newkey rsa:2048 -nodes -out ./secret/cert.pem -keyout ./secret/key.pem -days 365 -subj "/C=FR/O=krkr/OU=Domain Control Validated/CN=*" FROM gplane/pnpm:8.6.0-node16-alpine diff --git a/docker-compose.yml b/docker-compose.yml index e111dfcfe..9e7f93aac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,27 @@ services: - "8080:8080" # required only for public access - "28015:28015" # dashboard - required only for public access + inkvisitor-development: + build: + context: . + dockerfile: Dockerfile + args: + ENV: development + image: inkvisitor:development + container_name: inkvisitor-development + restart: always + <<: *logging + env_file: + - ./packages/server/env/.env.development + environment: + - DB_HOST=database + ports: + - "3000:3000" + volumes: + - ./inkvisitor-development-client:/app/client/dist + networks: + - inkvisitor + inkvisitor-production: build: context: . @@ -67,13 +88,9 @@ services: image: inkvisitor:sandbox container_name: inkvisitor-sandbox restart: always - logging: - options: - max-size: "10m" - max-file: "2" - # needs to be present when building - # env_file: - # - ./packages/server/env/.env.sandbox + <<: *logging + env_file: + - ./packages/server/env/.env.sandbox ports: - "3011:3000" volumes: diff --git a/docs/notes_1.3.x.md b/docs/notes_1.3.x.md index ffe86da94..d4788324f 100644 --- a/docs/notes_1.3.x.md +++ b/docs/notes_1.3.x.md @@ -1,6 +1,13 @@ -## InkVisitor 1.3.1 - 1.3.3 notes +## InkVisitor 1.3.x notes Throughout versions 1.3.1 to 1.3.3, the InkVisitor application has seen a series of improvements, both in functionality and user experience. While new features have consistently been added to simplify data handling and improve interface usability, there's been a strong emphasis on fixing identified bugs to ensure smooth operations. Simultaneously, the underlying code has undergone regular refactoring to optimize performance and incorporate the latest tech updates. +Besides minor improvements and a number of bug fixes, the main enhancements in the 1.3.x versions are: + +- New filtering on the Territory tree. +- Actants are now validated based on valency rules. +- Relations in Entities are now validated based on logical constraints. +- Modals in the Statement editor have been redesigned to use a system of toggles and buttons. +- Batch actions have been added for the Statement list and Statement Editor. ### InkVisitor Update Summary for each minor version diff --git a/docs/release_1.3.1.md b/docs/release_1.3.1.md index e4d84b355..1e4c7637e 100644 --- a/docs/release_1.3.1.md +++ b/docs/release_1.3.1.md @@ -2,29 +2,29 @@ ### New features: -- Batch actions on Statements in Territory #1566 -- Buttons to replace / append content from previous or from selected statement #1607 -- Searching (in Search box) by language #1651 -- A new button + modal for creating new entity from Detail box #1648 -- Dragging to an already occupied place (you can replace entity tag directly by drag-dropping another entity tag) #1640 -- User can choose their default language which is applied as language parameter for all new entities #1626 -- Add semantic relations to the entity tooltip #1624 -- Users can hide / display ordering table in Statement Editor (through user customization modal) #1654 -- Display entity tags even for entities with invalid data (but show that they are invalid) #1657 +- Batch actions on Statements in Territory (#1566) +- Buttons to replace / append content from previous or from selected statement (#1607) +- Searching (in Search box) by language (#1651) +- A new button + modal for creating new entity from Detail box (#1648) +- Dragging to an already occupied place (you can replace entity tag directly by drag-dropping another entity tag) (#1640) +- User can choose their default language which is applied as language parameter for all new entities (#1626) +- Add semantic relations to the entity tooltip (#1624) +- Users can hide / display ordering table in Statement Editor (through user customization modal) (#1654) +- Display entity tags even for entities with invalid data (but show that they are invalid) (#1657) -### Bugs: +### Bug fixes: -- Hiding tooltips when dragging entity #1643 -- Hide those inverse relation types which are empty #1608 -- Removed searching (in Seach box) by used template #1651 -- C a A are now created with status pending #1638 -- Reset password Toast allows copy the new password to clipboard #1637 +- Hiding tooltips when dragging entity (#1643) +- Hide those inverse relation types which are empty (#1608) +- Removed searching (in Seach box) by used template (#1651) +- C a A are now created with status pending (#1638) +- Reset password Toast allows copy the new password to clipboard (#1637) - "Search language" option hidden from the User customization modal - Suggester does not lists Entities with Value class - Territory tree box refreshes after a new entity class T is created ### Code refactor: -- Unlink button in Entity tags #1650 -- Box scrolling update and styling #1641 -- Data-import code rewritten, allows importing only selected collections #1635 +- Unlink button in Entity tags (#1650) +- Box scrolling update and styling (#1641) +- Data-import code rewritten, allows importing only selected collections (#1635) diff --git a/docs/release_1.3.2.md b/docs/release_1.3.2.md index 0f815ff55..d61da896c 100644 --- a/docs/release_1.3.2.md +++ b/docs/release_1.3.2.md @@ -2,29 +2,29 @@ ### New features: -- Attributes of the actants / actions / props are now displayed next to the Entity - modal windows for handling attributes are removed #1633 -- New icon symbolizing the connection latency to the server #1681 -- Handling relations in template entitiy details #1262 -- Batch actions for editing references in Statements #1666 -- Select entity class by key down first #1662 -- Autofocus on suggester entity class dropdown #1667 -- User can hide entity sorting in Statement Editor #1589 -- Assigning parent for new instances of T and S #1675 -- Footer removed #1695 +- Attributes of the actants / actions / props are now displayed next to the Entity - modal windows for handling attributes are removed (#1633) +- New icon symbolizing the connection latency to the server (#1681) +- Handling relations in template entitiy details (#1262) +- Batch actions for editing references in Statements (#1666) +- Select entity class by key down first (#1662) +- Autofocus on suggester entity class dropdown (#1667) +- User can hide entity sorting in Statement Editor (#1589) +- Assigning parent for new instances of T and S (#1675) +- Footer removed (#1695) -### Bugs: +### Bug fixes: -- Show bundle start and bundle end attributes #1668 -- Empty relations leading to deleted entities bug #1665 -- Smaller space for note #1688 -- Actions missing in search dropdown bug #1697 -- Username updated when changed in user settings #1729 +- Show bundle start and bundle end attributes (#1668) +- Empty relations leading to deleted entities bug (#1665) +- Smaller space for note (#1688) +- Actions missing in search dropdown bug (#1697) +- Username updated when changed in user settings (#1729) - Fixed non-breaking space symbols in the text fields -- Wrong date format in dates #1513 +- Wrong date format in dates (#1513) ### Code refactor: -- Merge interfaces IOption and DropdownItem #1653 -- Inactive user is stored as a logged-out user in db #1504 -- Updating react-query library to the latest version #1723 -- React-portal library is applied to the dropdowns resulting in better UX #1699 +- Merge interfaces IOption and DropdownItem (#1653) +- Inactive user is stored as a logged-out user in db (#1504) +- Updating react-query library to the latest version (#1723) +- React-portal library is applied to the dropdowns resulting in better UX (#1699) diff --git a/docs/release_1.3.3.md b/docs/release_1.3.3.md index d19ecf7fc..e737ea620 100644 --- a/docs/release_1.3.3.md +++ b/docs/release_1.3.3.md @@ -2,44 +2,44 @@ ### New features: -- Filtering Territories functionality in the Territory tree -- Improved design for Territory tree -- Integrated improveded warnings for Entities and Statements -- Enabled copying Statement ID from Editor box #1727 -- SOL relations generalized to SOE relations #1832 -- New modality: Expectation #1830 -- Enabled creating cross-entity-type Identification #1735 -- Enabled combining two SYN clouds #1774 -- Props design reworked #1750 #17 51 -- Improved password management #1789 -- Improved assigning T to users #1833 -- Pause connection to the DB on no internet # 1802 -- Statement tag tooltip improved #1824 +- Added functionality for filtering Territories in the Territory tree. +- Redesigned the Territory tree for improved user experience. +- Integrated enhanced warnings for Entities and Statements. +- Introduced feature to copy Statement ID directly from the Editor box. (#1727) +- Generalized SOL relations to SOE relations. (#1832) +- Introduced new modality: Expectation. (#1830) +- Enabled creation of cross-entity-type Identification. (#1735) +- Introduced feature to combine two SYN clouds. (#1774) +- Reworked Props design in Editor. (#1750, #1751) +- Enhanced password management system. (#1789) +- Improved the process of assigning Ts to users. (#1833) +- Implemented automatic pause of connection to the DB during internet outages. (#1802) +- Enhanced the Statement tag tooltip. (#1824) -### Bugs: +### Bug fixes: -- Username not refreshed when changed in user settings #1729 -- Removed unnecessary scroll in empty Actions list #1744 -- Some inversed relations not showing up #1740 -- C and A should not have IDE Relation (Identification) #1749 -- Territory invalid move #1745 -- Expanded meta props incorrectly aligned #1746 -- Avoid copying legacyId parameter #1752 and inversed relations #1753 -- Hide scrollbars when not needed #1758 -- Graph tooltip appearing with problems #1777 -- Removed some duplicates in array fields in data #1787 -- Value class unavailable for creation #1795 -- Deleting favorited T makes model invalid bug #1818 -- T template with missing parent applies incorrectly #1834 -- A wrong default select value in the "Create entity" modal #1838 -- Problem with handling audits for deleted users #1797 +- Fixed issue where username was not updating after being changed in user settings. (#1729) +- Removed unnecessary scroll in an empty Actions list. (#1744) +- Addressed issues with some inversed relations not displaying. (#1740) +- Ensured C and A no longer have the IDE Relation (Identification). (#1749) +- Fixed issue with invalid Territory moves. (#1745) +- Corrected alignment of expanded meta props. (#1746) +- Prevented copying of legacyId parameter and inversed relations. (#1752, #1753) +- Improved UI by hiding scrollbars when not in use. (#1758) +- Addressed issues with Graph tooltip display. (#1777) +- Removed duplicates in array fields within data. (#1787) +- Addressed unavailability of Value class for creation. (#1795) +- Fixed bug where deleting a favorited T invalidates the model. (#1818) +- Resolved issue with T template incorrectly applying when parent is missing. (#1834) +- Fixed default select value error in the "Create entity" modal. (#1838) +- Addressed issues in handling audits for deleted users. (#1797) ### Code refactor: -- Pnpm is now used for bundling and package management #1767 -- Npm scripts in packages (client, server, database) were standardized #1767 -- RethinkDB now uses pool connection to reduce the overhead #1756 -- Used package caching in CI #1772 -- Checked ts-ignore for react-dnd #1773 -- React-table migrated to v7 #1779 -- React-router fully upgraded to a new version +- Transitioned to pnpm for bundling and package management. (#1767) +- Standardized npm scripts across packages (client, server, database). (#1767) +- Optimized RethinkDB by using pool connection to reduce overhead. (#1756) +- Implemented package caching in CI for efficiency. (#1772) +- Addressed ts-ignore checks for react-dnd. (#1773) +- Upgraded React-table to version 7. (#1779) +- Completed upgrade of React-router to its latest version. diff --git a/packages/client/pnpm-lock.yaml b/packages/client/pnpm-lock.yaml index 8492b5440..03e989e3b 100644 --- a/packages/client/pnpm-lock.yaml +++ b/packages/client/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -230,6 +230,13 @@ packages: dependencies: '@babel/highlight': 7.18.6 + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + /@babel/compat-data@7.22.3: resolution: {integrity: sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==} engines: {node: '>=6.9.0'} @@ -246,7 +253,7 @@ packages: '@babel/helpers': 7.22.3 '@babel/parser': 7.22.4 '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4(supports-color@5.5.0) + '@babel/traverse': 7.23.2(supports-color@5.5.0) '@babel/types': 7.22.4 convert-source-map: 1.9.0 debug: 4.3.4(supports-color@5.5.0) @@ -265,6 +272,15 @@ packages: '@jridgewell/trace-mapping': 0.3.18 jsesc: 2.5.2 + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + jsesc: 2.5.2 + /@babel/helper-annotate-as-pure@7.18.6: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} engines: {node: '>=6.9.0'} @@ -339,6 +355,10 @@ packages: resolution: {integrity: sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==} engines: {node: '>=6.9.0'} + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + /@babel/helper-function-name@7.21.0: resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} engines: {node: '>=6.9.0'} @@ -346,12 +366,25 @@ packages: '@babel/template': 7.21.9 '@babel/types': 7.22.4 + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.22.4 + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + /@babel/helper-member-expression-to-functions@7.22.3: resolution: {integrity: sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==} engines: {node: '>=6.9.0'} @@ -374,7 +407,7 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4(supports-color@5.5.0) + '@babel/traverse': 7.23.2(supports-color@5.5.0) '@babel/types': 7.22.4 transitivePeerDependencies: - supports-color @@ -411,7 +444,7 @@ packages: '@babel/helper-member-expression-to-functions': 7.22.3 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4(supports-color@5.5.0) + '@babel/traverse': 7.23.2(supports-color@5.5.0) '@babel/types': 7.22.4 transitivePeerDependencies: - supports-color @@ -434,14 +467,28 @@ packages: dependencies: '@babel/types': 7.22.4 + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + /@babel/helper-string-parser@7.21.5: resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==} engines: {node: '>=6.9.0'} + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-option@7.21.0: resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} engines: {node: '>=6.9.0'} @@ -452,7 +499,7 @@ packages: dependencies: '@babel/helper-function-name': 7.21.0 '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4(supports-color@5.5.0) + '@babel/traverse': 7.23.2(supports-color@5.5.0) '@babel/types': 7.22.4 transitivePeerDependencies: - supports-color @@ -462,7 +509,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4(supports-color@5.5.0) + '@babel/traverse': 7.23.2(supports-color@5.5.0) '@babel/types': 7.22.4 transitivePeerDependencies: - supports-color @@ -475,6 +522,14 @@ packages: chalk: 2.4.2 js-tokens: 4.0.0 + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + /@babel/parser@7.22.4: resolution: {integrity: sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==} engines: {node: '>=6.0.0'} @@ -482,6 +537,13 @@ packages: dependencies: '@babel/types': 7.22.4 + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.0 + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.22.1): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} @@ -505,11 +567,12 @@ packages: /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.22.1): resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.22.1 - '@babel/helper-environment-visitor': 7.22.1 + '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.22.1) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.1) @@ -519,6 +582,7 @@ packages: /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.22.1): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -541,6 +605,7 @@ packages: /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.22.1): resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -551,6 +616,7 @@ packages: /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.22.1): resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -564,6 +630,7 @@ packages: /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.22.1): resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -574,6 +641,7 @@ packages: /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.22.1): resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1584,18 +1652,26 @@ packages: '@babel/parser': 7.22.4 '@babel/types': 7.22.4 - /@babel/traverse@7.22.4(supports-color@5.5.0): - resolution: {integrity: sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==} + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.22.3 - '@babel/helper-environment-visitor': 7.22.1 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.22.4 - '@babel/types': 7.22.4 + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + + /@babel/traverse@7.23.2(supports-color@5.5.0): + resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: @@ -1609,6 +1685,14 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true @@ -2691,8 +2775,8 @@ packages: /@types/babel__core@7.20.1: resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==} dependencies: - '@babel/parser': 7.22.4 - '@babel/types': 7.22.4 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.20.1 @@ -2701,20 +2785,20 @@ packages: /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.22.4 + '@babel/types': 7.23.0 dev: true /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.22.4 - '@babel/types': 7.22.4 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 dev: true /@types/babel__traverse@7.20.1: resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==} dependencies: - '@babel/types': 7.22.4 + '@babel/types': 7.23.0 dev: true /@types/body-parser@1.19.2: @@ -3657,8 +3741,8 @@ packages: resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/template': 7.21.9 - '@babel/types': 7.22.4 + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 '@types/babel__core': 7.20.1 '@types/babel__traverse': 7.20.1 dev: true @@ -4346,12 +4430,12 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.24) - postcss: 8.4.24 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.24) - postcss-modules-local-by-default: 4.0.3(postcss@8.4.24) - postcss-modules-scope: 3.0.0(postcss@8.4.24) - postcss-modules-values: 4.0.0(postcss@8.4.24) + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.31) + postcss-modules-local-by-default: 4.0.3(postcss@8.4.31) + postcss-modules-scope: 3.0.0(postcss@8.4.31) + postcss-modules-values: 4.0.0(postcss@8.4.31) postcss-value-parser: 4.2.0 semver: 7.5.1 webpack: 5.85.0(webpack-cli@5.1.1) @@ -5621,13 +5705,13 @@ packages: safer-buffer: 2.1.2 dev: true - /icss-utils@5.1.0(postcss@8.4.24): + /icss-utils@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: true /ieee754@1.2.1: @@ -5929,7 +6013,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.22.1 - '@babel/parser': 7.22.4 + '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -6301,11 +6385,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.22.1 - '@babel/generator': 7.22.3 + '@babel/generator': 7.23.0 '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.22.1) '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.22.1) - '@babel/traverse': 7.22.4(supports-color@5.5.0) - '@babel/types': 7.22.4 + '@babel/traverse': 7.23.2(supports-color@5.5.0) + '@babel/types': 7.23.0 '@jest/expect-utils': 29.5.0 '@jest/transform': 29.5.0 '@jest/types': 29.5.0 @@ -6460,7 +6544,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.22.1 - '@babel/parser': 7.22.4 + '@babel/parser': 7.23.0 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.1) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.1) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.1) @@ -6862,7 +6946,7 @@ packages: '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.22.1) '@babel/plugin-transform-typescript': 7.22.3(@babel/core@7.22.1) '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.22.1) - '@babel/template': 7.21.9 + '@babel/template': 7.22.15 react-refresh: 0.4.3 transitivePeerDependencies: - supports-color @@ -6896,8 +6980,8 @@ packages: /metro-source-map@0.73.9: resolution: {integrity: sha512-l4VZKzdqafipriETYR6lsrwtavCF1+CMhCOY9XbyWeTrpGSNgJQgdeJpttzEZTHQQTLR0csQo0nD1ef3zEP6IQ==} dependencies: - '@babel/traverse': 7.22.4(supports-color@5.5.0) - '@babel/types': 7.22.4 + '@babel/traverse': 7.23.2(supports-color@5.5.0) + '@babel/types': 7.23.0 invariant: 2.2.4 metro-symbolicate: 0.73.9 nullthrows: 1.1.1 @@ -6925,9 +7009,9 @@ packages: resolution: {integrity: sha512-r9NeiqMngmooX2VOKLJVQrMuV7PAydbqst5bFhdVBPcFpZkxxqyzjzo+kzrszGy2UpSQBZr2P1L6OMjLHwQwfQ==} dependencies: '@babel/core': 7.22.1 - '@babel/generator': 7.22.3 - '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4(supports-color@5.5.0) + '@babel/generator': 7.23.0 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2(supports-color@5.5.0) nullthrows: 1.1.1 transitivePeerDependencies: - supports-color @@ -6936,9 +7020,9 @@ packages: resolution: {integrity: sha512-Rq4b489sIaTUENA+WCvtu9yvlT/C6zFMWhU4sq+97W29Zj0mPBjdk+qGT5n1ZBgtBIJzZWt1KxeYuc17f4aYtQ==} dependencies: '@babel/core': 7.22.1 - '@babel/generator': 7.22.3 - '@babel/parser': 7.22.4 - '@babel/types': 7.22.4 + '@babel/generator': 7.23.0 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 babel-preset-fbjs: 3.4.0(@babel/core@7.22.1) metro: 0.73.9 metro-babel-transformer: 0.73.9 @@ -6958,13 +7042,13 @@ packages: resolution: {integrity: sha512-BlYbPmTF60hpetyNdKhdvi57dSqutb+/oK0u3ni4emIh78PiI0axGo7RfdsZ/mn3saASXc94tDbpC5yn7+NpEg==} hasBin: true dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.22.13 '@babel/core': 7.22.1 - '@babel/generator': 7.22.3 - '@babel/parser': 7.22.4 - '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4(supports-color@5.5.0) - '@babel/types': 7.22.4 + '@babel/generator': 7.23.0 + '@babel/parser': 7.23.0 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2(supports-color@5.5.0) + '@babel/types': 7.23.0 absolute-path: 0.0.0 accepts: 1.3.8 async: 3.2.4 @@ -7506,45 +7590,45 @@ packages: resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} engines: {node: '>=0.10.0'} - /postcss-modules-extract-imports@3.0.0(postcss@8.4.24): + /postcss-modules-extract-imports@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: true - /postcss-modules-local-by-default@4.0.3(postcss@8.4.24): + /postcss-modules-local-by-default@4.0.3(postcss@8.4.31): resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.24) - postcss: 8.4.24 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: true - /postcss-modules-scope@3.0.0(postcss@8.4.24): + /postcss-modules-scope@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true - /postcss-modules-values@4.0.0(postcss@8.4.24): + /postcss-modules-values@4.0.0(postcss@8.4.31): resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.24) - postcss: 8.4.24 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 dev: true /postcss-selector-parser@6.0.13: @@ -7558,8 +7642,8 @@ packages: /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - /postcss@8.4.24: - resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==} + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -7695,8 +7779,8 @@ packages: pure-color: 1.3.0 dev: false - /react-devtools-core@4.27.8: - resolution: {integrity: sha512-KwoH8/wN/+m5wTItLnsgVraGNmFrcTWR3k1VimP1HjtMMw4CNF+F5vg4S/0tzTEKIdpCi2R7mPNTC+/dswZMgw==} + /react-devtools-core@4.28.4: + resolution: {integrity: sha512-IUZKLv3CimeM07G3vX4H4loxVpByrzq3HvfTX7v9migalwvLs9ZY5D3S3pKR33U+GguYfBBdMMZyToFhsSE/iQ==} dependencies: shell-quote: 1.8.1 ws: 7.5.9 @@ -7817,7 +7901,7 @@ packages: /react-native-codegen@0.71.5(@babel/preset-env@7.22.4): resolution: {integrity: sha512-rfsuc0zkuUuMjFnrT55I1mDZ+pBRp2zAiRwxck3m6qeGJBGK5OV5JH66eDQ4aa+3m0of316CqrJDRzVlYufzIg==} dependencies: - '@babel/parser': 7.22.4 + '@babel/parser': 7.23.0 flow-parser: 0.185.2 jscodeshift: 0.13.1(@babel/preset-env@7.22.4) nullthrows: 1.1.1 @@ -7859,7 +7943,7 @@ packages: pretty-format: 26.6.2 promise: 8.3.0 react: 18.2.0 - react-devtools-core: 4.27.8 + react-devtools-core: 4.28.4 react-native-codegen: 0.71.5(@babel/preset-env@7.22.4) react-native-gradle-plugin: 0.71.18 react-refresh: 0.4.3 @@ -8901,7 +8985,7 @@ packages: react-is: '>= 16.8.0' dependencies: '@babel/helper-module-imports': 7.21.4 - '@babel/traverse': 7.22.4(supports-color@5.5.0) + '@babel/traverse': 7.23.2(supports-color@5.5.0) '@emotion/is-prop-valid': 1.2.1 '@emotion/stylis': 0.8.5 '@emotion/unitless': 0.7.5 diff --git a/packages/client/src/Theme/constants.ts b/packages/client/src/Theme/constants.ts index 623ede898..aeb0f00b3 100644 --- a/packages/client/src/Theme/constants.ts +++ b/packages/client/src/Theme/constants.ts @@ -1,7 +1,7 @@ import { EntityEnums } from "@shared/enums"; import theme from "./theme"; -export const defaultPing = -2; +export const defaultPing = -10; export const DropdownAny = "any"; export const wildCardChar = "*"; diff --git a/packages/client/src/Theme/theme.ts b/packages/client/src/Theme/theme.ts index bad679519..96797c77d 100644 --- a/packages/client/src/Theme/theme.ts +++ b/packages/client/src/Theme/theme.ts @@ -92,6 +92,7 @@ const theme = { }, ping: { + "-2": "black", "-1": "black", "0": "#d73027", "1": "#fc8d59", diff --git a/packages/client/src/api.tsx b/packages/client/src/api.tsx index c07b34cb9..7d2bec653 100644 --- a/packages/client/src/api.tsx +++ b/packages/client/src/api.tsx @@ -24,13 +24,18 @@ import { import * as errors from "@shared/types/errors"; import { IRequestSearch } from "@shared/types/request-search"; import { defaultPing } from "Theme/constants"; -import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; +import axios, { + AxiosError, + AxiosInstance, + AxiosRequestConfig, + AxiosResponse, +} from "axios"; import React from "react"; import { toast } from "react-toastify"; import io, { Socket } from "socket.io-client"; interface IApiOptions extends AxiosRequestConfig { - ignoreErrorToast: boolean + ignoreErrorToast: boolean; } type IFilterUsers = { @@ -114,6 +119,7 @@ class Api { console.error("Socket error:", error); }); this.ws.on("connect_error", (error) => { + this.ping = -2; console.error("Socket connection error:", error); }); this.ws.on("connect_timeout", () => { @@ -177,7 +183,9 @@ class Api { message: "", }; - if (responseData && typeof responseData === "object") { + if (responseData instanceof AxiosError && (responseData as AxiosError).code === AxiosError.ERR_NETWORK) { + out.error = errors.NetworkError.name + } else if (responseData && (responseData as any).response && (responseData as any).response.data) { out.error = (responseData as Record).error; out.message = (responseData as Record).message; } @@ -187,7 +195,7 @@ class Api { showErrorToast(err: any) { const hydratedError = errors.getErrorByCode( - this.responseToError(err.response?.data) + this.responseToError(err) ); toast.error( @@ -246,10 +254,10 @@ class Api { * @returns Api */ withoutToaster() { - const newApi = new Api() - newApi.token = this.token + const newApi = new Api(); + newApi.token = this.token; newApi.useDefaultRequestInterceptors(); // required for login - return newApi + return newApi; } async signIn(username: string, password: string): Promise { @@ -290,7 +298,10 @@ class Api { /** * Users */ - async usersGet(userId: string, options?: IApiOptions): Promise> { + async usersGet( + userId: string, + options?: IApiOptions + ): Promise> { try { const response = await this.connection.get(`/users/${userId}`, options); return response; @@ -300,7 +311,8 @@ class Api { } async usersGetMore( - filters: IFilterUsers, options?: IApiOptions + filters: IFilterUsers, + options?: IApiOptions ): Promise> { try { const response = await this.connection.get( @@ -313,10 +325,13 @@ class Api { } } - async usersCreate(userData: { - name: string; - email: string; - }, options?: IApiOptions): Promise> { + async usersCreate( + userData: { + name: string; + email: string; + }, + options?: IApiOptions + ): Promise> { try { const response = await this.connection.post(`/users`, userData, options); return response; @@ -331,16 +346,26 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.put(`/users/${userId}`, changes, options); + const response = await this.connection.put( + `/users/${userId}`, + changes, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; } } - async usersDelete(userId: string, options?: IApiOptions): Promise> { + async usersDelete( + userId: string, + options?: IApiOptions + ): Promise> { try { - const response = await this.connection.delete(`/users/${userId}`, options); + const response = await this.connection.delete( + `/users/${userId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -355,14 +380,17 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.patch(`/users/${userId}/password`, undefined, options); + const response = await this.connection.patch( + `/users/${userId}/password`, + undefined, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; } } - /* This request will update the password of the user represented by userId Optionally use "me" as placeholder for the userId @@ -373,9 +401,13 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.put(`/users/${userId}`, { - password - }, options); + const response = await this.connection.put( + `/users/${userId}`, + { + password, + }, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -407,9 +439,14 @@ class Api { * Administration * Administration container */ - async administrationGet(options?: IApiOptions): Promise> { + async administrationGet( + options?: IApiOptions + ): Promise> { try { - const response = await this.connection.get(`/users/administration`, options); + const response = await this.connection.get( + `/users/administration`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -425,7 +462,10 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.get(`/users/${userId}/bookmarks`, options); + const response = await this.connection.get( + `/users/${userId}/bookmarks`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -436,9 +476,15 @@ class Api { * Entities * Suggester container */ - async entitiesGet(entityId: string, options?: IApiOptions): Promise> { + async entitiesGet( + entityId: string, + options?: IApiOptions + ): Promise> { try { - const response = await this.connection.get(`/entities/${entityId}`, options); + const response = await this.connection.get( + `/entities/${entityId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -464,10 +510,15 @@ class Api { } async entityCreate( - newEntityData: IEntity | IStatement | ITerritory, options?: IApiOptions + newEntityData: IEntity | IStatement | ITerritory, + options?: IApiOptions ): Promise> { try { - const response = await this.connection.post(`/entities`, newEntityData, options); + const response = await this.connection.post( + `/entities`, + newEntityData, + options + ); return response; } catch (err: any | AxiosError) { console.log(err); @@ -481,7 +532,9 @@ class Api { ): Promise> { try { const response = await this.connection.post( - `/entities/${originalId}/clone`, undefined, options, + `/entities/${originalId}/clone`, + undefined, + options ); return response; } catch (err: any | AxiosError) { @@ -512,7 +565,10 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.delete(`/entities/${entityId}`, options); + const response = await this.connection.delete( + `/entities/${entityId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -520,10 +576,12 @@ class Api { } async entityRestore( - entityId: string, + entityId: string ): Promise> { try { - const response = await this.connection.post(`/entities/${entityId}/restore`); + const response = await this.connection.post( + `/entities/${entityId}/restore` + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -534,10 +592,14 @@ class Api { * Detail * Detail container */ - async detailGet(entityId: string, options?: IApiOptions): Promise> { + async detailGet( + entityId: string, + options?: IApiOptions + ): Promise> { try { const response = await this.connection.get( - `/entities/${entityId}/detail`, options + `/entities/${entityId}/detail`, + options ); return response; } catch (err: any | AxiosError) { @@ -566,10 +628,14 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.patch(`/tree/${moveId}/position`, { - parentId, - newIndex, - }, options); + const response = await this.connection.patch( + `/tree/${moveId}/position`, + { + parentId, + newIndex, + }, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -585,7 +651,10 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.get(`/territories/${territoryId}`, options); + const response = await this.connection.get( + `/territories/${territoryId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -602,7 +671,8 @@ class Api { ): Promise> { try { const response = await this.connection.get( - `/territories/${territoryId}/entities`, options + `/territories/${territoryId}/entities`, + options ); return response; } catch (err: any | AxiosError) { @@ -620,7 +690,8 @@ class Api { ): Promise> { try { const response = await this.connection.get( - `/entities/${entityId}/tooltip`, options + `/entities/${entityId}/tooltip`, + options ); return response; } catch (err: any | AxiosError) { @@ -631,7 +702,10 @@ class Api { /** * Audit */ - async auditGet(entityId: string, options?: IApiOptions): Promise> { + async auditGet( + entityId: string, + options?: IApiOptions + ): Promise> { try { const response = await this.connection.get( `/entities/${entityId}/audits`, @@ -652,7 +726,10 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.get(`/statements/${statementId}`, options); + const response = await this.connection.get( + `/statements/${statementId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -753,7 +830,9 @@ class Api { * Pernmissions */ - async getAclPermissions(options?: IApiOptions): Promise> { + async getAclPermissions( + options?: IApiOptions + ): Promise> { try { const response = await this.connection.get(`/acls`, options); return response; @@ -768,14 +847,21 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.put(`/acls/${permissionId}`, data, options); + const response = await this.connection.put( + `/acls/${permissionId}`, + data, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; } } - async activate(hash: string, options?: IApiOptions): Promise> { + async activate( + hash: string, + options?: IApiOptions + ): Promise> { try { const response = await this.connection.patch( `/users/active?hash=${hash}`, @@ -813,7 +899,11 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.post(`/relations`, newRelation, options); + const response = await this.connection.post( + `/relations`, + newRelation, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -825,7 +915,10 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.delete(`/relations/${relationId}`, options); + const response = await this.connection.delete( + `/relations/${relationId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -856,16 +949,25 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.get(`/documents/${documentId}`, options); + const response = await this.connection.get( + `/documents/${documentId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; } } - async documentDelete(documentId: string, options?: IApiOptions): Promise> { + async documentDelete( + documentId: string, + options?: IApiOptions + ): Promise> { try { - const response = await this.connection.delete(`/documents/${documentId}`, options); + const response = await this.connection.delete( + `/documents/${documentId}`, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -880,7 +982,11 @@ class Api { options?: IApiOptions ): Promise> { try { - const response = await this.connection.post(`/documents`, document, options); + const response = await this.connection.post( + `/documents`, + document, + options + ); return response; } catch (err: any | AxiosError) { throw { ...err.response.data }; @@ -909,9 +1015,9 @@ class Api { } const apiSingleton = new Api(); -apiSingleton.initWs() +apiSingleton.initWs(); apiSingleton.checkLogin(); apiSingleton.useDefaultRequestInterceptors(); apiSingleton.useDefaultResponseInterceptors(); -export default apiSingleton +export default apiSingleton; diff --git a/packages/client/src/assets/logos/eu_msmt.png b/packages/client/src/assets/logos/eu_msmt.png new file mode 100644 index 000000000..609e30037 Binary files /dev/null and b/packages/client/src/assets/logos/eu_msmt.png differ diff --git a/packages/client/src/components/advanced/PageHeader/PageHeader.tsx b/packages/client/src/components/advanced/PageHeader/PageHeader.tsx index d6b01c3a5..7763bb74e 100644 --- a/packages/client/src/components/advanced/PageHeader/PageHeader.tsx +++ b/packages/client/src/components/advanced/PageHeader/PageHeader.tsx @@ -1,10 +1,11 @@ +import { useQueryClient } from "@tanstack/react-query"; import { heightHeader } from "Theme/constants"; import { PingColor } from "Theme/theme"; import LogoInkvisitor from "assets/logos/inkvisitor.svg"; import { Loader } from "components"; import React, { useEffect, useState } from "react"; -import { useQueryClient } from "@tanstack/react-query"; -import { useNavigate, useLocation } from "react-router"; +import { useLocation, useNavigate } from "react-router"; +import { BeatLoader } from "react-spinners"; import { toast } from "react-toastify"; import { useAppSelector } from "redux/hooks"; import { Menu } from ".."; @@ -46,9 +47,21 @@ export const LeftHeader: React.FC = React.memo( const ping: number = useAppSelector((state) => state.ping); const [pingColor, setPingColor] = useState("0"); + const [waitingForServerRestart, setWaitingForServerRestart] = + useState(false); useEffect(() => { + if ((ping === -1 || ping === -2) && !waitingForServerRestart) { + setWaitingForServerRestart(true); + } else if (ping >= 0 && waitingForServerRestart) { + queryClient.invalidateQueries(); + setWaitingForServerRestart(false); + } + switch (true) { + case ping === -2: + setPingColor("-2"); + return; case ping === -1: setPingColor("-1"); return; @@ -101,11 +114,20 @@ export const LeftHeader: React.FC = React.memo( - {ping === -2 && "loading..."} + {ping === -10 && "loading"} + {ping === -2 && "Connection to server failed"} {ping === -1 && "Server is down"} {ping >= 0 && `Server connection latency:`} - {ping >= -1 && } + {ping === -10 && ( + + )} + {ping >= -2 && } {ping >= 0 && {ping}ms} diff --git a/packages/client/src/components/basic/Button/Button.tsx b/packages/client/src/components/basic/Button/Button.tsx index 582f16620..c1f99e7ec 100644 --- a/packages/client/src/components/basic/Button/Button.tsx +++ b/packages/client/src/components/basic/Button/Button.tsx @@ -19,6 +19,7 @@ interface ButtonProps { tooltipContent?: ReactElement[] | ReactElement; label?: string; icon?: JSX.Element | EntityEnums.Operator; + iconRight?: JSX.Element | EntityEnums.Operator; noIconMargin?: boolean; noBackground?: boolean; inverted?: boolean; @@ -38,6 +39,7 @@ export const Button: React.FC = ({ tooltipContent, label = "", icon, + iconRight, noIconMargin = false, inverted = false, noBorder = false, @@ -82,6 +84,7 @@ export const Button: React.FC = ({ {label} )} + {iconRight} {(tooltipLabel || tooltipContent) && ( diff --git a/packages/client/src/components/basic/Button/ButtonStyles.tsx b/packages/client/src/components/basic/Button/ButtonStyles.tsx index 7769543b3..84e79eac8 100644 --- a/packages/client/src/components/basic/Button/ButtonStyles.tsx +++ b/packages/client/src/components/basic/Button/ButtonStyles.tsx @@ -59,5 +59,5 @@ export const StyledButtonLabel = styled.span<{ noIconMargin?: boolean; }>` margin-left: ${({ theme, hasIcon = false, noIconMargin = false }) => - hasIcon ? (noIconMargin ? 0 : theme.space[2]) : 0}; + hasIcon ? (noIconMargin ? 0 : "0.3rem") : 0}; `; diff --git a/packages/client/src/components/basic/Dropdown/Dropdown.tsx b/packages/client/src/components/basic/Dropdown/Dropdown.tsx index 2ce7a8bff..a50a3ec1b 100644 --- a/packages/client/src/components/basic/Dropdown/Dropdown.tsx +++ b/packages/client/src/components/basic/Dropdown/Dropdown.tsx @@ -23,7 +23,7 @@ import { Tooltip } from "components"; interface Dropdown { options?: DropdownItem[]; - value?: DropdownItem | DropdownItem[]; + value?: DropdownItem | DropdownItem[] | null; onChange: (selectedOption: DropdownItem[]) => void; components?: any; ref?: React.RefObject; @@ -76,7 +76,8 @@ export const Dropdown: React.FC = ({ allowAny = false, }) => { const optionsWithIterator = options[Symbol.iterator](); - const isOneOptionSingleSelect = options.length < 2 && !isMulti; + const isOneOptionSingleEntitySelect = + options.length < 2 && !isMulti && entityDropdown; const [displayValue, setDisplayValue] = useState(value); useEffect(() => { @@ -102,8 +103,8 @@ export const Dropdown: React.FC = ({ autoFocus={autoFocus} onBlur={onBlur} isMulti={isMulti} - isDisabled={disabled || isOneOptionSingleSelect} - isOneOptionSingleSelect={isOneOptionSingleSelect} + isDisabled={disabled || isOneOptionSingleEntitySelect} + isOneOptionSingleEntitySelect={isOneOptionSingleEntitySelect} entityDropdown={entityDropdown} wildCardChar={(value as DropdownItem)?.label === "*"} className="react-select-container" @@ -129,10 +130,12 @@ export const Dropdown: React.FC = ({ dropdownIndicator: () => { return { display: - noDropDownIndicator || isOneOptionSingleSelect ? "none" : "", + noDropDownIndicator || isOneOptionSingleEntitySelect + ? "none" + : "", }; }, - menuPortal: (base: any) => ({ + menuPortal: (base) => ({ ...base, marginTop: `${-heightHeader}px`, zIndex: 9999, @@ -145,6 +148,10 @@ export const Dropdown: React.FC = ({ ? selected : [selected]; + if (!isMulti) { + return onChange(selectedOptions); + } + if (selectedOptions !== null && selectedOptions.length > 0) { // kdyz je neco vybrany = aspon jeden option if (attributeDropdown && event.action === "remove-value") { diff --git a/packages/client/src/components/basic/Dropdown/DropdownStyles.tsx b/packages/client/src/components/basic/Dropdown/DropdownStyles.tsx index 69fad946a..1fdd058db 100644 --- a/packages/client/src/components/basic/Dropdown/DropdownStyles.tsx +++ b/packages/client/src/components/basic/Dropdown/DropdownStyles.tsx @@ -23,7 +23,7 @@ export const StyledSelectWrapper = styled.div` interface StyledSelect { width?: number | "full"; disabled?: boolean; - isOneOptionSingleSelect?: boolean; + isOneOptionSingleEntitySelect?: boolean; suggester?: boolean; entityDropdown?: boolean; wildCardChar?: boolean; @@ -57,8 +57,8 @@ export const StyledSelect = styled(Select)` } } .react-select__control--is-disabled { - background: ${({ isOneOptionSingleSelect }) => - isOneOptionSingleSelect + background: ${({ isOneOptionSingleEntitySelect }) => + isOneOptionSingleEntitySelect ? "" : `repeating-linear-gradient( -45deg, diff --git a/packages/client/src/components/basic/Message/Message.tsx b/packages/client/src/components/basic/Message/Message.tsx index db3659523..37975aff8 100644 --- a/packages/client/src/components/basic/Message/Message.tsx +++ b/packages/client/src/components/basic/Message/Message.tsx @@ -3,6 +3,7 @@ import { IEntity, IWarning } from "@shared/types"; import React, { useEffect, useState } from "react"; import { TiWarningOutline } from "react-icons/ti"; import { StyledMessage } from "./MessateStyles"; +import { getShortLabelByLetterCount } from "utils"; interface Message { warning: IWarning; @@ -48,7 +49,9 @@ export const Message: React.FC = ({ warning, entities }) => { Missing{" "} - {position?.section ? positionObject[position?.section] : "actant"} + {position?.subSection + ? positionObject[position?.subSection] + : "actant"} :{" "} {"at least one actant of a matching type should be used"} @@ -58,31 +61,47 @@ export const Message: React.FC = ({ warning, entities }) => { return ( {`Actant's entity type does not match the Action`} - {position?.section && ` - ${positionObject[position?.section]}`} - {entity && ` - [${entity.class}: ${entity.label}]`} + {position?.subSection && + ` - ${positionObject[position?.subSection]}`} + {entity && + ` - [${entity.class}: ${getShortLabelByLetterCount( + entity.label, + 200 + )}]`} ); case WarningTypeEnums.ANA: return ( {`This actant position allows no actant`} - {position?.section && ` - ${positionObject[position?.section]}`} - {entity && ` - [${entity.class}: ${entity.label}]`} + {position?.subSection && + ` - ${positionObject[position?.subSection]}`} + {entity && + ` - [${entity.class}: ${getShortLabelByLetterCount( + entity.label, + 200 + )}]`} ); case WarningTypeEnums.WAC: return ( {`Entity type valencies of the actions not matching`} - {position?.section && ` - ${positionObject[position?.section]}`} + {position?.subSection && + ` - ${positionObject[position?.subSection]}`} ); case WarningTypeEnums.AVU: return ( {`Action valency not defined`} - {position?.section && ` - ${positionObject[position?.section]}`} - {entity && ` - [${entity.class}: ${entity.label}]`} + {position?.subSection && + ` - ${positionObject[position?.subSection]}`} + {entity && + ` - [${entity.class}: ${getShortLabelByLetterCount( + entity.label, + 200 + )}]`} ); @@ -98,7 +117,13 @@ export const Message: React.FC = ({ warning, entities }) => { case WarningTypeEnums.MVAL: return Missing at least one entity-type valency; case WarningTypeEnums.AVAL: - return Asymmetrical valency; + return ( + + Asymmetrical valency + {position?.subSection && + ` - ${positionObject[position?.subSection]}`} + + ); case WarningTypeEnums.MAEE: return Missing action/event equivalent; default: @@ -108,7 +133,9 @@ export const Message: React.FC = ({ warning, entities }) => { return ( - +
+ +
{getWarningMessage(warning)}
); diff --git a/packages/client/src/components/basic/Modal/ModalStyles.tsx b/packages/client/src/components/basic/Modal/ModalStyles.tsx index ea7ba5e69..364ab163d 100644 --- a/packages/client/src/components/basic/Modal/ModalStyles.tsx +++ b/packages/client/src/components/basic/Modal/ModalStyles.tsx @@ -94,9 +94,6 @@ export const StyledCardBody = styled.section` padding: ${space5} ${space7}; overflow: ${({ enableScroll }) => (enableScroll ? "auto" : "initial")}; font-size: ${({ theme }) => theme.fontSize["sm"]}; - * { - user-select: text; - } `; interface StyledFooter {} export const StyledFooter = styled.div` diff --git a/packages/client/src/hooks/useParamsContext.tsx b/packages/client/src/hooks/useParamsContext.tsx index 97eeb4d5e..b90592cd8 100644 --- a/packages/client/src/hooks/useParamsContext.tsx +++ b/packages/client/src/hooks/useParamsContext.tsx @@ -23,6 +23,7 @@ const INITIAL_CONTEXT = { selectedDetailId: "", setSelectedDetailId: UNINITIALISED, appendDetailId: UNINITIALISED, + appendMultipleDetailIds: UNINITIALISED, removeDetailId: UNINITIALISED, clearAllDetailIds: UNINITIALISED, cleanAllParams: UNINITIALISED, @@ -36,6 +37,7 @@ interface SearchParamsContext { selectedDetailId: string; setSelectedDetailId: (id: string) => void; appendDetailId: (id: string) => void; + appendMultipleDetailIds: (ids: string[]) => void; removeDetailId: (id: string) => void; clearAllDetailIds: () => void; cleanAllParams: () => void; @@ -114,6 +116,34 @@ export const SearchParamsProvider = ({ setTimeout(() => setSelectedDetailId(id), 100); }; + const appendMultipleDetailIds = (ids: string[]) => { + const detailIdArray = getDetailIdArray(); + let newDetailIdArray: string[] = []; + + if (ids.length === 10) { + // use as it is + newDetailIdArray = ids; + } else if (ids.length > 10) { + // cut and use the first 10 + newDetailIdArray = ids.slice(0, maxTabCount); + } else { + // merge with existing + // remove already added ids and add them at the end + const filteredArray: string[] = detailIdArray.filter( + (id) => !ids.includes(id) + ); + newDetailIdArray = filteredArray.concat(ids); + if (newDetailIdArray.length > maxTabCount) { + newDetailIdArray = newDetailIdArray.slice( + newDetailIdArray.length - maxTabCount + ); + } + } + + setDetailId(newDetailIdArray.join(arrJoinChar)); + setTimeout(() => setSelectedDetailId(ids[0]), 100); + }; + const removeDetailId = (id: string) => { const detailIdArray = getDetailIdArray(); const index = detailIdArray.indexOf(id); @@ -228,6 +258,7 @@ export const SearchParamsProvider = ({ selectedDetailId, setSelectedDetailId, appendDetailId, + appendMultipleDetailIds, removeDetailId, clearAllDetailIds, cleanAllParams, diff --git a/packages/client/src/pages/About/AboutStyles.tsx b/packages/client/src/pages/About/AboutStyles.tsx index 11a1fa07d..9599d3e56 100644 --- a/packages/client/src/pages/About/AboutStyles.tsx +++ b/packages/client/src/pages/About/AboutStyles.tsx @@ -1,47 +1,54 @@ +import { space5, space7 } from "Theme/constants"; import styled from "styled-components"; -import theme from "Theme/theme"; -export const StyledModalContentWrapper = styled.div` - display: block; +export const StyledContentWrapper = styled.div` + display: flex; + align-items: center; + flex-direction: column; + overflow: auto; width: 100%; - overflow-x: visible; - overflow-y: visible; +`; +export const StyledContent = styled.div` + display: flex; + flex-direction: column; + height: 100%; + padding: ${space5} ${space7}; + font-size: ${({ theme }) => theme.fontSize["sm"]}; `; -export const StyledModalLogo = styled.div` +export const StyledLogo = styled.div` background-color: ${({ theme }) => theme.color.primary}; padding: ${({ theme }) => theme.space[8]}; `; -export const StyledModalTitle = styled.div` +export const StyledTitle = styled.div` font-size: ${({ theme }) => theme.fontSize["3xl"]}; font-weight: ${({ theme }) => theme.fontWeight["bold"]}; `; -export const StyledModalSubTitle = styled.div` +export const StyledSubTitle = styled.div` font-size: ${({ theme }) => theme.fontSize["xl"]}; font-weight: ${({ theme }) => theme.fontWeight["italic"]}; - `; -export const StyledModalContent = styled.div``; -export const StyledModalHeader = styled.div` +`; +export const StyledHeader = styled.div` font-family: Muni; font-size: ${({ theme }) => theme.fontSize["xl"]}; color: ${({ theme }) => theme.color["primary"]}; padding-top: ${({ theme }) => theme.space[4]}; `; -export const StyledModalText = styled.div` +export const StyledText = styled.div` padding-bottom: ${({ theme }) => theme.space[2]}; `; -export const StyledModalTextList = styled.ul` -padding-left: ${({ theme }) => theme.space[10]}; +export const StyledTextList = styled.ul` + padding-left: ${({ theme }) => theme.space[10]}; `; -export const StyledModalTextListItem = styled.li` -padding-top: ${({ theme }) => theme.space[2]}; +export const StyledTextListItem = styled.li` + padding-top: ${({ theme }) => theme.space[2]}; `; -export const StyledModalPerson = styled.div``; -export const StyledModalLink = styled.span` +export const StyledPerson = styled.div``; +export const StyledLink = styled.span` display: inline; margin-left: ${({ theme }) => theme.space[1]}; margin-right: ${({ theme }) => theme.space[0]}; @@ -55,9 +62,9 @@ export const StyledModalLink = styled.span` } `; -export const StyledModalAcknowledgement = styled.div``; +export const StyledAcknowledgement = styled.div``; -export const StyledModalAcknowledgementLogo = styled.img` - margin:${({ theme }) => theme.space[4]}; - padding:${({ theme }) => theme.space[2]}; +export const StyledAcknowledgementLogo = styled.img` + margin: ${({ theme }) => theme.space[4]}; + padding: ${({ theme }) => theme.space[2]}; `; diff --git a/packages/client/src/pages/About/index.tsx b/packages/client/src/pages/About/index.tsx index 7ae6f8799..e9b673907 100644 --- a/packages/client/src/pages/About/index.tsx +++ b/packages/client/src/pages/About/index.tsx @@ -1,47 +1,37 @@ -import { - Button, - ButtonGroup, - Modal, - ModalContent, - ModalFooter, - ModalHeader, -} from "components"; import React from "react"; import { - StyledModalAcknowledgement, - StyledModalAcknowledgementLogo, - StyledModalContent, - StyledModalContentWrapper, - StyledModalHeader, - StyledModalLink, - StyledModalLogo, - StyledModalText, - StyledModalTextListItem, - StyledModalTextList, + StyledAcknowledgement, + StyledAcknowledgementLogo, + StyledContent, + StyledContentWrapper, + StyledHeader, + StyledLink, + StyledLogo, + StyledText, + StyledTextList, + StyledTextListItem, } from "./AboutStyles"; import LogoInkvisitor from "assets/logos/inkvisitor-full.svg"; -import { AiOutlineLink } from "react-icons/ai"; -import { HiLink } from "react-icons/hi"; import { FaUserAlt } from "react-icons/fa"; +import { HiLink } from "react-icons/hi"; import { MdMail } from "react-icons/md"; const LogoGACR = require("assets/logos/gacr-en_rgb.png"); const LogoERC = require("assets/logos/logo_erc-flag_eum.png"); const LogoMUNI = require("assets/logos/arts-muni.png"); +const LogoEUMSMT = require("assets/logos/eu_msmt.png"); interface IAcknowledgementLogo { src: string; url: string; - } const AcknowledgementLogo: React.FC = ({ src, url }) => { return ( - + - ) -} + ); +}; interface IMailWithIcon { label: string; @@ -49,12 +39,11 @@ interface IMailWithIcon { } const IMailWithIcon: React.FC = ({ label, url }) => { return ( - + - = - {label} + ={label} - + ); }; interface ILinkWithIcon { @@ -64,12 +53,12 @@ interface ILinkWithIcon { const LinkWithIcon: React.FC = ({ label, url }) => { return ( - + {label} - + ); }; @@ -80,131 +69,204 @@ interface IPersonWithIcon { const PersonWithIcon: React.FC = ({ label, url }) => { return ( - + {label} - + ); }; -interface IAboutPage { -} +interface IAboutPage {} -export const AboutPage: React.FC = ({ }) => { +export const AboutPage: React.FC = ({}) => { return ( -
- - - - - - - - About - - - - - InkVisitor is an open-source, browser-based research environment for linked data. It is designed to assist researchers in the humanities and social sciences in transforming texts into complex structured data for further exploration and analysis. - - - InkVisitor implements a specific method of text-oriented data collection: Computer-Assisted Semantic Text Modelling (CASTEMO). CASTEMO is highly attentive to semantic detail and the qualities of original expression. - - - The data are entered in the form of entities and statements. Statements relate entities of various types into semantic quadruples (subject, verb, object 1, object 2) following the syntactic structure of texts. - - - InkVisitor serves as a data-entry front-end for JSON-format research databases (). These databases allow for complex queries and render the data available for various kinds of computational analyses. - - - InkVisitor has been developed by the ERC-funded Dissident Networks Project (). - - - InkVisitor is distributed under an open license (the ). - - - Please post feature requests and report bugs on our . - - - - - - {/* */} - - - Credits - - - - The lead authors of the CASTEMO data collection workflow and data model are , and . - Other authors include and . - - - The lead developer of InkVisitor is . Other developers include , , , and others. - - - Testers of InkVisitor and contributors to specific parts of the data model include , , and others. - - - - Funding - - - European Research Council (project No. 101000442 “Networks of Dissent: Computational Modelling of Dissident and Inquisitorial Cultures in Medieval Europe (DISSINET)”, 2021–2026) - - - Czech Science Foundation (EXPRO project No. GX19-26975X “Dissident Religious Cultures in Medieval Europe from the Perspective of Social Network Analysis and Geographic Information Systems”, 2019–2021) - - - Masaryk University, Faculty of Arts (project “InkVisitor Development: Towards a Project-Neutral Open-Source Research Application for the Collection of Structured Relational Data from Texts”, 2022–2023) - - - - - Cite InkVisitor - - - - Zbíral, David; Mertel, Adam; Hanák, Petr; Mertel, Ján; Ondrejka, Peter; Hampejs, Tomáš; and Shaw, Robert L. J. (2022). InkVisitor. Available online at: + + + + + +
+ About + + + + InkVisitor is an{" "} + + open-source, browser-based research environment for linked + data + + . It is designed to assist researchers in the humanities and + social sciences in{" "} + + transforming texts into complex structured data for further + exploration and analysis. + + + + InkVisitor implements a specific method of text-oriented data + collection:{" "} + Computer-Assisted Semantic Text Modelling (CASTEMO). + CASTEMO is{" "} + + highly attentive to semantic detail and the qualities of + original expression. + + + + The data are entered in the form of entities and{" "} + statements. Statements relate entities of various types + into semantic quadruples (subject, verb, object 1, object + 2) following the syntactic structure of texts. + + + InkVisitor serves as a data-entry front-end for JSON-format{" "} + research databases ( + + ). These databases allow for complex queries and render the data + available for various kinds of computational analyses. + + + InkVisitor has been developed by the{" "} + ERC-funded Dissident Networks Project ( + + ). + + + InkVisitor is distributed under an open license (the{" "} + + ). + + + Please post feature requests and report bugs on our{" "} + + . + + + + {/* */} + + + Credits + + + + The{" "} + + lead authors of the CASTEMO data collection workflow and data + model + {" "} + are , and{" "} + . Other authors + include and{" "} + . + + + The lead developer of InkVisitor is{" "} + . Other developers + include ,{" "} + ,{" "} + , and others. + + + Testers of InkVisitor and{" "} + contributors to specific parts of the data model include{" "} + ,{" "} + , and others. + + + + Funding + + + European Research Council (project No. 101000442 “Networks + of Dissent: Computational Modelling of Dissident and Inquisitorial + Cultures in Medieval Europe (DISSINET)”, 9/2021–8/2026) + + + Czech Science Foundation (EXPRO project No. GX19-26975X + “Dissident Religious Cultures in Medieval Europe from the + Perspective of Social Network Analysis and Geographic Information + Systems”, 1/2019–8/2021) + + + Masaryk University, Faculty of Arts (project “InkVisitor + Development: Towards a Project-Neutral Open-Source Research + Application for the Collection of Structured Relational Data from + Texts”, 7/2022–8/2023) + + + + Czech Ministry of Education, Youth and Sports & European Union + {" "} + (project “Beyond Security: Role of Conflict in + Resilience-Building”, 9/2023–6/2028) + + + + Cite InkVisitor + + + + + Zbíral, David; Mertel, Adam; Hanák, Petr; Mertel, Ján; Ondrejka, + Peter; Hampejs, Tomáš; and Shaw, Robert L. J. (2022). + InkVisitor. Available online at: + . - - + /> + . + + + + Cite CASTEMO - Cite CASTEMO - - - - Zbíral, David; Shaw, Robert L. J.; Hampejs, Tomáš; & Mertel, Adam. (2022). Model the source first! Towards Computer-Assisted Semantic Text Modelling and source criticism 2.0. Zenodo. + + + Zbíral, David; Shaw, Robert L. J.; Hampejs, Tomáš; & Mertel, + Adam. (2022). Model the source first! Towards Computer-Assisted + Semantic Text Modelling and source criticism 2.0. Zenodo. + - - - - - - - - - - - -
+ /> + + + +
+ + + + + + + + + ); }; diff --git a/packages/client/src/pages/MainPage/containers/EntityBookmarkBox/EntityBookmarkTable/EntityBookmarkTableRow.tsx b/packages/client/src/pages/MainPage/containers/EntityBookmarkBox/EntityBookmarkTable/EntityBookmarkTableRow.tsx index d35a4f508..81323af04 100644 --- a/packages/client/src/pages/MainPage/containers/EntityBookmarkBox/EntityBookmarkTable/EntityBookmarkTableRow.tsx +++ b/packages/client/src/pages/MainPage/containers/EntityBookmarkBox/EntityBookmarkTable/EntityBookmarkTableRow.tsx @@ -7,13 +7,13 @@ import { useDrop, } from "react-dnd"; import { FaGripVertical } from "react-icons/fa"; -import { Cell, ColumnInstance } from "react-table"; +import { ColumnInstance, Row } from "react-table"; import { DragItem, ItemTypes } from "types"; import { dndHoverFn } from "utils"; import { StyledTd, StyledTr } from "./EntityBookmarkTableStyles"; interface EntityBookmarkTableRow { - row: any; + row: Row; index: number; moveRow: (dragIndex: number, hoverIndex: number) => void; folder: IResponseBookmarkFolder; @@ -67,7 +67,7 @@ export const EntityBookmarkTableRow: React.FC = ({ ) : ( )} - {row.cells.map((cell: Cell) => { + {row.cells.map((cell) => { return ( {cell.render("Cell")} ); diff --git a/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetail.tsx b/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetail.tsx index 656a1988c..77fd3f55e 100644 --- a/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetail.tsx +++ b/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetail.tsx @@ -39,6 +39,7 @@ import { StyledDetailSectionContentUsedIn, StyledDetailSectionEntityList, StyledDetailSectionHeader, + StyledDetailWarnings, StyledDetailWrapper, StyledPropGroupWrap, StyledUsedAsHeading, @@ -50,6 +51,7 @@ import { EntityDetailMetaPropsTable } from "./EntityDetailUsedInTable/EntityDeta import { EntityDetailStatementPropsTable } from "./EntityDetailUsedInTable/EntityDetailStatementPropsTable/EntityDetailStatementPropsTable"; import { EntityDetailStatementsTable } from "./EntityDetailUsedInTable/EntityDetailStatementsTable/EntityDetailStatementsTable"; import { EntityDetailValency } from "./EntityDetailValency/EntityDetailValency"; +import { IWarningPositionSection } from "@shared/types/warning"; const allowedEntityChangeClasses = [ EntityEnums.Class.Value, @@ -155,27 +157,16 @@ export const EntityDetail: React.FC = ({ detailId }) => { ); const templateOptions: DropdownItem[] = useMemo(() => { - const options = [ - { - value: "", - label: "select template", - }, - ]; - - if (entity !== undefined && templates) { - templates - .filter((template) => template.id !== entity.id) - .forEach((template) => { - const maxLetterCount = 200; - options.push({ - value: template.id, - label: getShortLabelByLetterCount( - getEntityLabel(template), - maxLetterCount - ), - }); - }); - } + const options = + entity !== undefined && templates + ? templates + .filter((template) => template.id !== entity.id) + .map((template) => ({ + value: template.id, + label: getShortLabelByLetterCount(getEntityLabel(template), 200), + })) + : []; + return options; }, [templates, entity]); @@ -219,12 +210,36 @@ export const EntityDetail: React.FC = ({ detailId }) => { ); }, [entity]); + const { + status: statusStatement, + data: statement, + error: statementError, + isFetching: isFetchingStatement, + } = useQuery( + ["statement", statementId], + async () => { + const res = await api.statementGet(statementId); + return res.data; + }, + { enabled: !!statementId && api.isLoggedIn() } + ); + const updateEntityMutation = useMutation( async (changes: any) => await api.entityUpdate(detailId, changes), { onSuccess: (data, variables) => { queryClient.invalidateQueries(["entity"]); + if ( + statementId && + (statementId === entity?.id || + (statement?.entities && + entity && + Object.keys(statement.entities).includes(entity.id))) + ) { + queryClient.invalidateQueries(["statement"]); + } + if ( variables.references !== undefined || variables.detail !== undefined || @@ -236,9 +251,7 @@ export const EntityDetail: React.FC = ({ detailId }) => { if (entity?.class === EntityEnums.Class.Territory) { queryClient.invalidateQueries(["tree"]); } - if (entity?.class === EntityEnums.Class.Statement) { - queryClient.invalidateQueries(["statement"]); - } + queryClient.invalidateQueries(["territory"]); queryClient.invalidateQueries(["bookmarks"]); } @@ -247,6 +260,10 @@ export const EntityDetail: React.FC = ({ detailId }) => { } if (entity?.isTemplate) { queryClient.invalidateQueries(["templates"]); + queryClient.invalidateQueries(["entity-templates"]); + if (entity?.class === EntityEnums.Class.Statement) { + queryClient.invalidateQueries(["statement-templates"]); + } } }, } @@ -606,6 +623,18 @@ export const EntityDetail: React.FC = ({ detailId }) => { {entity.class === EntityEnums.Class.Action && ( Valency + + {entity.warnings && + entity.warnings + .filter( + (w) => + w.position?.section === + IWarningPositionSection.Valencies + ) + .map((warning, key) => { + return ; + })} + = ({ detailId }) => { {/* Relations */} Relations - {entity.warnings && - entity.warnings.map((warning, key) => { - return ; - })} + + {entity.warnings && + entity.warnings + .filter( + (w) => + w.position?.section === + IWarningPositionSection.Relations + ) + .map((warning, key) => { + return ; + })} + = ({ { handleAskForTemplateApply(templateToApply[0]); }} diff --git a/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetailStyles.tsx b/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetailStyles.tsx index 510a7e016..158d05c7f 100644 --- a/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetailStyles.tsx +++ b/packages/client/src/pages/MainPage/containers/EntityDetailBox/EntityDetail/EntityDetailStyles.tsx @@ -36,6 +36,12 @@ export const StyledDetailSectionHeader = styled.div` color: ${({ theme }) => theme.color["primary"]}; `; +export const StyledDetailWarnings = styled.div` + display: grid; + grid-gap: ${({ theme }) => theme.space["1"]}; + grid-auto-flow: row; +`; + export const StyledDetailContentRowValueID = styled.div` display: inline-flex; font-style: italic; diff --git a/packages/client/src/pages/MainPage/containers/StatementEditorBox/StatementEditor/StatementEditor.tsx b/packages/client/src/pages/MainPage/containers/StatementEditorBox/StatementEditor/StatementEditor.tsx index 2729d9034..7f01699b0 100644 --- a/packages/client/src/pages/MainPage/containers/StatementEditorBox/StatementEditor/StatementEditor.tsx +++ b/packages/client/src/pages/MainPage/containers/StatementEditorBox/StatementEditor/StatementEditor.tsx @@ -18,7 +18,6 @@ import { Button, Dropdown, Input, - Loader, Message, MultiInput, Submit, @@ -40,15 +39,22 @@ import { } from "constructors"; import { useSearchParams } from "hooks"; import React, { useEffect, useMemo, useState } from "react"; -import { AiOutlineWarning } from "react-icons/ai"; +import { + AiOutlineCaretDown, + AiOutlineCaretUp, + AiOutlineWarning, +} from "react-icons/ai"; import { FaRegCopy } from "react-icons/fa"; +import { TiWarningOutline } from "react-icons/ti"; import { toast } from "react-toastify"; -import { useAppSelector } from "redux/hooks"; +import { setShowWarnings } from "redux/features/statementEditor/showWarningsSlice"; +import { useAppDispatch, useAppSelector } from "redux/hooks"; import { DropdownItem, classesEditorActants, classesEditorTags } from "types"; import { getEntityLabel, getShortLabelByLetterCount } from "utils"; import { EntityReferenceTable } from "../../EntityReferenceTable/EntityReferenceTable"; import { StyledBreadcrumbWrap, + StyledDetailWarnings, StyledEditorContentRow, StyledEditorContentRowLabel, StyledEditorContentRowValue, @@ -70,7 +76,6 @@ import { StatementEditorActantTable } from "./StatementEditorActantTable/Stateme import { StatementEditorActionTable } from "./StatementEditorActionTable/StatementEditorActionTable"; import { StatementEditorOrdering } from "./StatementEditorOrdering/StatementEditorOrdering"; import { StatementEditorSectionButtons } from "./StatementEditorSectionButtons/StatementEditorSectionButtons"; -import { TiWarningOutline } from "react-icons/ti"; interface StatementEditor { statement: IResponseStatement; @@ -89,8 +94,13 @@ export const StatementEditor: React.FC = ({ updateStatementDataMutation, moveStatementMutation, }) => { - const { statementId, territoryId, setTerritoryId, appendDetailId } = - useSearchParams(); + const { + statementId, + territoryId, + setTerritoryId, + appendDetailId, + appendMultipleDetailIds, + } = useSearchParams(); const queryClient = useQueryClient(); @@ -195,27 +205,15 @@ export const StatementEditor: React.FC = ({ ); const templateOptions: DropdownItem[] = useMemo(() => { - const options = [ - { - value: "", - label: "select template", - }, - ]; - - if (templates) { - templates - .filter((template) => template.id !== statement.id) - .forEach((template) => { - const maxLetterCount = 200; - options.push({ + const options = templates + ? templates + .filter((template) => template.id !== statement.id) + .map((template) => ({ value: template.id, - label: getShortLabelByLetterCount( - getEntityLabel(template), - maxLetterCount - ), - }); - }); - } + label: getShortLabelByLetterCount(getEntityLabel(template), 200), + })) + : []; + return options; }, [templates, statement]); @@ -552,7 +550,19 @@ export const StatementEditor: React.FC = ({ const [showSubmitSection, setShowSubmitSection] = useState< "actants" | "actions" | "references" | false >(false); - const [showWarnings, setShowWarnings] = useState(false); + + const showWarnings = useAppSelector( + (state) => state.statementEditor.showWarnings + ); + const dispatch = useAppDispatch(); + + useEffect(() => { + if (showWarnings && statement.data.actions.length > 0) { + appendMultipleDetailIds( + Array.from(new Set(statement.data.actions.map((a) => a.actionId))) + ); + } + }, [showWarnings, statementId]); return ( <> @@ -624,7 +634,6 @@ export const StatementEditor: React.FC = ({ )} )} - )} @@ -651,10 +660,11 @@ export const StatementEditor: React.FC = ({ { handleAskForTemplateApply(templateToApply[0]); }} @@ -691,27 +701,40 @@ export const StatementEditor: React.FC = ({ {statement.warnings.length > 0 && ( -