diff --git a/create_config_prod.js b/create_config_prod.js index 79465301..a54eaf9d 100644 --- a/create_config_prod.js +++ b/create_config_prod.js @@ -1,22 +1,22 @@ -const parseBoolean = (value) => ["true", "1", "yes"].includes((value || "").toLocaleLowerCase()); +const parseBoolean = (value) => ['true', '1', 'yes'].includes((value || '').toLocaleLowerCase()); const siteConfig = { - CLIENT_NAME: process.env.BENTO_PUBLIC_CLIENT_NAME || null, - PORTAL_URL: process.env.BENTO_PUBLIC_PORTAL_URL || null, - TRANSLATED: parseBoolean(process.env.BENTO_PUBLIC_TRANSLATED), - BEACON_URL: process.env.BEACON_URL || null, - BEACON_UI_ENABLED: parseBoolean(process.env.BENTO_BEACON_UI_ENABLED), + CLIENT_NAME: process.env.BENTO_PUBLIC_CLIENT_NAME || null, + PORTAL_URL: process.env.BENTO_PUBLIC_PORTAL_URL || null, + TRANSLATED: parseBoolean(process.env.BENTO_PUBLIC_TRANSLATED), + BEACON_URL: process.env.BEACON_URL || null, + BEACON_UI_ENABLED: parseBoolean(process.env.BENTO_BEACON_UI_ENABLED), - // Authentication - PUBLIC_URL: process.env.BENTO_PUBLIC_URL || null, - CLIENT_ID: process.env.CLIENT_ID || null, - OPENID_CONFIG_URL: process.env.OPENID_CONFIG_URL || null, + // Authentication + PUBLIC_URL: process.env.BENTO_PUBLIC_URL || null, + CLIENT_ID: process.env.CLIENT_ID || null, + OPENID_CONFIG_URL: process.env.OPENID_CONFIG_URL || null, }; -if (typeof require !== "undefined" && require.main === module) { - process.stdout.write(`BENTO_PUBLIC_CONFIG = ${JSON.stringify(siteConfig, null, 2)};\n`); +if (typeof require !== 'undefined' && require.main === module) { + process.stdout.write(`BENTO_PUBLIC_CONFIG = ${JSON.stringify(siteConfig, null, 2)};\n`); } module.exports = { - siteConfig, + siteConfig, }; diff --git a/package-lock.json b/package-lock.json index c18a3efc..8e370525 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,9 +80,9 @@ } }, "node_modules/@ant-design/cssinjs": { - "version": "1.18.4", - "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.18.4.tgz", - "integrity": "sha512-IrUAOj5TYuMG556C9gdbFuOrigyhzhU5ZYpWb3gYTxAwymVqRbvLzFCZg6OsjLBR6GhzcxYF3AhxKmjB+rA2xA==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.21.0.tgz", + "integrity": "sha512-gIilraPl+9EoKdYxnupxjHB/Q6IHNRjEXszKbDxZdsgv4sAZ9pjkCq8yanDWNvyfjp4leir2OVAJm0vxwKK8YA==", "dependencies": { "@babel/runtime": "^7.11.1", "@emotion/hash": "^0.8.0", @@ -98,9 +98,9 @@ } }, "node_modules/@ant-design/icons": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.3.4.tgz", - "integrity": "sha512-U5eTSeSFr2V9SeJzYOo5mybAZfsoNuiIA8bvFoZUe+h9LBLs8UwrVaVwcMQC4AhBuojXkLMlmtnIlvUczXXHaQ==", + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.3.7.tgz", + "integrity": "sha512-bCPXTAg66f5bdccM4TT21SQBDO1Ek2gho9h3nO9DAKXJP4sq+5VBjrQMSxMVXSB3HyEz+cUbHQ5+6ogxCOpaew==", "dependencies": { "@ant-design/colors": "^7.0.0", "@ant-design/icons-svg": "^4.4.0", @@ -122,9 +122,9 @@ "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==" }, "node_modules/@ant-design/react-slick": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.0.2.tgz", - "integrity": "sha512-Wj8onxL/T8KQLFFiCA4t8eIRGpRR+UPgOdac2sYzonv+i0n3kXHmvHLLiOYL655DQx2Umii9Y9nNgL7ssu5haQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", "dependencies": { "@babel/runtime": "^7.10.4", "classnames": "^2.2.5", @@ -137,9 +137,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -418,6 +418,17 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@rc-component/async-validator": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", + "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, "node_modules/@rc-component/color-picker": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-1.5.3.tgz", @@ -492,9 +503,9 @@ } }, "node_modules/@rc-component/tour": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.14.2.tgz", - "integrity": "sha512-A75DZ8LVvahBIvxooj3Gvf2sxe+CGOkmzPNX7ek0i0AJHyKZ1HXe5ieIGo3m0FMdZfVOlbCJ952Duq8VKAHk6g==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.0.tgz", + "integrity": "sha512-h6hyILDwL+In9GAgRobwRWihLqqsD7Uft3fZGrJ7L4EiyCoxbnNYwzPXDfz7vNDhWeVyvAWQJj9fJCzpI4+b4g==", "dependencies": { "@babel/runtime": "^7.18.0", "@rc-component/portal": "^1.0.0-9", @@ -511,9 +522,9 @@ } }, "node_modules/@rc-component/trigger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.0.0.tgz", - "integrity": "sha512-niwKADPdY5dhdIblV6uwSayVivwo2uUISfJqri+/ovYQcH/omxDYBJKo755QKeoIIsWptxnRpgr7reEnNEZGFg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.0.tgz", + "integrity": "sha512-QarBCji02YE9aRFhZgRZmOpXBj0IZutRippsVBv85sxvG4FGk/vRxwAlkn3MS9zK5mwbETd86mAVg2tKqTkdJA==", "dependencies": { "@babel/runtime": "^7.23.2", "@rc-component/portal": "^1.1.0", @@ -1437,56 +1448,56 @@ } }, "node_modules/antd": { - "version": "5.15.3", - "resolved": "https://registry.npmjs.org/antd/-/antd-5.15.3.tgz", - "integrity": "sha512-53dpdGbfwipHVbqITmppp8N16i+BscMzz8NUNwaJgxwSvO9VQh/NfC/90lqGq3I2oBmxQ8TzRIxzFVKD/9OhlQ==", + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.18.2.tgz", + "integrity": "sha512-2RHNIaydjMcbvdHzn7/GRIcu1pfGkTBSqkQ7O5GGIwC+p086a5NFzKTmanRITFme5Iqn0If1sbWTt6XOywRKIg==", "dependencies": { "@ant-design/colors": "^7.0.2", - "@ant-design/cssinjs": "^1.18.4", - "@ant-design/icons": "^5.3.3", - "@ant-design/react-slick": "~1.0.2", - "@babel/runtime": "^7.24.0", + "@ant-design/cssinjs": "^1.20.0", + "@ant-design/icons": "^5.3.7", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.24.7", "@ctrl/tinycolor": "^3.6.1", "@rc-component/color-picker": "~1.5.3", "@rc-component/mutate-observer": "^1.1.0", - "@rc-component/tour": "~1.14.2", - "@rc-component/trigger": "^2.0.0", + "@rc-component/tour": "~1.15.0", + "@rc-component/trigger": "^2.2.0", "classnames": "^2.5.1", "copy-to-clipboard": "^3.3.3", - "dayjs": "^1.11.10", + "dayjs": "^1.11.11", "qrcode.react": "^3.1.0", - "rc-cascader": "~3.24.0", - "rc-checkbox": "~3.2.0", - "rc-collapse": "~3.7.2", - "rc-dialog": "~9.4.0", - "rc-drawer": "~7.1.0", + "rc-cascader": "~3.26.0", + "rc-checkbox": "~3.3.0", + "rc-collapse": "~3.7.3", + "rc-dialog": "~9.5.2", + "rc-drawer": "~7.2.0", "rc-dropdown": "~4.2.0", - "rc-field-form": "~1.42.1", - "rc-image": "~7.6.0", - "rc-input": "~1.4.5", - "rc-input-number": "~9.0.0", - "rc-mentions": "~2.11.1", - "rc-menu": "~9.13.0", - "rc-motion": "^2.9.0", - "rc-notification": "~5.3.0", + "rc-field-form": "~2.2.1", + "rc-image": "~7.9.0", + "rc-input": "~1.5.1", + "rc-input-number": "~9.1.0", + "rc-mentions": "~2.14.0", + "rc-menu": "~9.14.0", + "rc-motion": "^2.9.1", + "rc-notification": "~5.6.0", "rc-pagination": "~4.0.4", - "rc-picker": "~4.3.0", - "rc-progress": "~3.5.1", - "rc-rate": "~2.12.0", + "rc-picker": "~4.5.0", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.0", "rc-resize-observer": "^1.4.0", "rc-segmented": "~2.3.0", - "rc-select": "~14.13.0", - "rc-slider": "~10.5.0", + "rc-select": "~14.14.0", + "rc-slider": "~10.6.2", "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", - "rc-table": "~7.42.0", - "rc-tabs": "~14.1.1", - "rc-textarea": "~1.6.3", + "rc-table": "~7.45.7", + "rc-tabs": "~15.1.1", + "rc-textarea": "~1.7.0", "rc-tooltip": "~6.2.0", - "rc-tree": "~5.8.5", - "rc-tree-select": "~5.19.0", + "rc-tree": "~5.8.8", + "rc-tree-select": "~5.21.0", "rc-upload": "~4.5.2", - "rc-util": "^5.39.1", + "rc-util": "^5.42.1", "scroll-into-view-if-needed": "^3.1.0", "throttle-debounce": "^5.0.0" }, @@ -1677,11 +1688,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/async-validator": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", - "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2614,9 +2620,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" }, "node_modules/debug": { "version": "4.3.4", @@ -6671,14 +6677,14 @@ } }, "node_modules/rc-cascader": { - "version": "3.24.0", - "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.24.0.tgz", - "integrity": "sha512-NwkYsVULA61S085jbOYbq8Z7leyIxVmLwf+71mWLjA3kCfUf/rAKC0WfjQbqBDaLGlU9d4z1EzyPaHBKLYWv6A==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.26.0.tgz", + "integrity": "sha512-L1dml383TPSJD1I11YwxuVbmqaJY64psZqFp1ETlgl3LEOwDu76Cyl11fw5dmjJhMlUWwM5dECQfqJgfebhUjg==", "dependencies": { "@babel/runtime": "^7.12.5", "array-tree-filter": "^2.1.0", "classnames": "^2.3.1", - "rc-select": "~14.13.0", + "rc-select": "~14.14.0", "rc-tree": "~5.8.1", "rc-util": "^5.37.0" }, @@ -6688,9 +6694,9 @@ } }, "node_modules/rc-checkbox": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.2.0.tgz", - "integrity": "sha512-8inzw4y9dAhZmv/Ydl59Qdy5tdp9CKg4oPVcRigi+ga/yKPZS5m5SyyQPtYSgbcqHRYOdUhiPSeKfktc76du1A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.3.0.tgz", + "integrity": "sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.3.2", @@ -6717,9 +6723,9 @@ } }, "node_modules/rc-dialog": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.4.0.tgz", - "integrity": "sha512-AScCexaLACvf8KZRqCPz12BJ8olszXOS4lKlkMyzDQHS1m0zj1KZMYgmMCh39ee0Dcv8kyrj8mTqxuLyhH+QuQ==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.5.2.tgz", + "integrity": "sha512-qVUjc8JukG+j/pNaHVSRa2GO2/KbV2thm7yO4hepQ902eGdYK913sGkwg/fh9yhKYV1ql3BKIN2xnud3rEXAPw==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/portal": "^1.0.0-8", @@ -6733,9 +6739,9 @@ } }, "node_modules/rc-drawer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.1.0.tgz", - "integrity": "sha512-nBE1rF5iZvpavoyqhSSz2mk/yANltA7g3aF0U45xkx381n3we/RKs9cJfNKp9mSWCedOKWt9FLEwZDaAaOGn2w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.2.0.tgz", + "integrity": "sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==", "dependencies": { "@babel/runtime": "^7.23.9", "@rc-component/portal": "^1.1.1", @@ -6764,12 +6770,12 @@ } }, "node_modules/rc-field-form": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.42.1.tgz", - "integrity": "sha512-SqiEmWNP+I61Lt80+ofPvT+3l8Ij6vb35IS+x14gheVnCJN0SRnOwEgsqCEB5FslT7xqjUqDnU845hRZ1jzlAA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.2.1.tgz", + "integrity": "sha512-uoNqDoR7A4tn4QTSqoWPAzrR7ZwOK5I+vuZ/qdcHtbKx+ZjEsTg7QXm2wk/jalDiSksAQmATxL0T5LJkRREdIA==", "dependencies": { "@babel/runtime": "^7.18.0", - "async-validator": "^4.1.0", + "@rc-component/async-validator": "^5.0.3", "rc-util": "^5.32.2" }, "engines": { @@ -6781,14 +6787,14 @@ } }, "node_modules/rc-image": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.6.0.tgz", - "integrity": "sha512-tL3Rvd1sS+frZQ01i+tkeUPaOeFz2iG9/scAt/Cfs0hyCRVA/w0Pu1J/JxIX8blalvmHE0bZQRYdOmRAzWu4Hg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.9.0.tgz", + "integrity": "sha512-l4zqO5E0quuLMCtdKfBgj4Suv8tIS011F5k1zBBlK25iMjjiNHxA0VeTzGFtUZERSA45gvpXDg8/P6qNLjR25g==", "dependencies": { "@babel/runtime": "^7.11.2", "@rc-component/portal": "^1.0.2", "classnames": "^2.2.6", - "rc-dialog": "~9.4.0", + "rc-dialog": "~9.5.2", "rc-motion": "^2.6.2", "rc-util": "^5.34.1" }, @@ -6798,9 +6804,9 @@ } }, "node_modules/rc-input": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.4.5.tgz", - "integrity": "sha512-AjzykhwnwYTRSwwgCu70CGKBIAv6bP2nqnFptnNTprph/TF1BAs0Qxl91mie/BR6n827WIJB6ZjaRf9iiMwAfw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.5.1.tgz", + "integrity": "sha512-+nOzQJDeIfIpNP/SgY45LXSKbuMlp4Yap2y8c+ZpU7XbLmNzUd6+d5/S75sA/52jsVE6S/AkhkkDEAOjIu7i6g==", "dependencies": { "@babel/runtime": "^7.11.1", "classnames": "^2.2.1", @@ -6812,15 +6818,15 @@ } }, "node_modules/rc-input-number": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.0.0.tgz", - "integrity": "sha512-RfcDBDdWFFetouWFXBA+WPEC8LzBXyngr9b+yTLVIygfFu7HiLRGn/s/v9wwno94X7KFvnb28FNynMGj9XJlDQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.1.0.tgz", + "integrity": "sha512-NqJ6i25Xn/AgYfVxynlevIhX3FuKlMwIFpucGG1h98SlK32wQwDK0zhN9VY32McOmuaqzftduNYWWooWz8pXQA==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/mini-decimal": "^1.0.1", "classnames": "^2.2.5", - "rc-input": "~1.4.0", - "rc-util": "^5.28.0" + "rc-input": "~1.5.0", + "rc-util": "^5.40.1" }, "peerDependencies": { "react": ">=16.9.0", @@ -6828,16 +6834,16 @@ } }, "node_modules/rc-mentions": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.11.1.tgz", - "integrity": "sha512-upb4AK1SRFql7qGnbLEvJqLMugVVIyjmwBJW9L0eLoN9po4JmJZaBzmKA4089fNtsU8k6l/tdZiVafyooeKnLw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.14.0.tgz", + "integrity": "sha512-qKR59FMuF8PK4ZqsbWX3UuA5P1M/snzyqV6Yt3y1DCFbCEdqUGIBgQp6vEfLCO6Z0RoRFlzXtCeSlBTcDDpg1A==", "dependencies": { "@babel/runtime": "^7.22.5", "@rc-component/trigger": "^2.0.0", "classnames": "^2.2.6", - "rc-input": "~1.4.0", - "rc-menu": "~9.13.0", - "rc-textarea": "~1.6.1", + "rc-input": "~1.5.0", + "rc-menu": "~9.14.0", + "rc-textarea": "~1.7.0", "rc-util": "^5.34.1" }, "peerDependencies": { @@ -6846,9 +6852,9 @@ } }, "node_modules/rc-menu": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.13.0.tgz", - "integrity": "sha512-1l8ooCB3HcYJKCltC/s7OxRKRjgymdl9htrCeGZcXNaMct0RxZRK6OPV3lPhVksIvAGMgzPd54ClpZ5J4b8cZA==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.14.0.tgz", + "integrity": "sha512-La3LBCDMLMs9Q/8mTGbnscb+ZeJ26ebkLz9xJFHd2SD8vfsCKl1Z/k3mwbxyKL01lB40fel1s9Nn9LAv/nmVJQ==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^2.0.0", @@ -6863,13 +6869,13 @@ } }, "node_modules/rc-motion": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.0.tgz", - "integrity": "sha512-XIU2+xLkdIr1/h6ohPZXyPBMvOmuyFZQ/T0xnawz+Rh+gh4FINcnZmMT5UTIj6hgI0VLDjTaPeRd+smJeSPqiQ==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.2.tgz", + "integrity": "sha512-fUAhHKLDdkAXIDLH0GYwof3raS58dtNUmzLF2MeiR8o6n4thNpSDQhOqQzWE4WfFZDCi9VEN8n7tiB7czREcyw==", "dependencies": { "@babel/runtime": "^7.11.1", "classnames": "^2.2.1", - "rc-util": "^5.21.0" + "rc-util": "^5.43.0" }, "peerDependencies": { "react": ">=16.9.0", @@ -6877,9 +6883,9 @@ } }, "node_modules/rc-notification": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.3.0.tgz", - "integrity": "sha512-WCf0uCOkZ3HGfF0p1H4Sgt7aWfipxORWTPp7o6prA3vxwtWhtug3GfpYls1pnBp4WA+j8vGIi5c2/hQRpGzPcQ==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.0.tgz", + "integrity": "sha512-TGQW5T7waOxLwgJG7fXcw8l7AQiFOjaZ7ISF5PrU526nunHRNcTMuzKihQHaF4E/h/KfOCDk3Mv8eqzbu2e28w==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", @@ -6924,9 +6930,9 @@ } }, "node_modules/rc-picker": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.3.0.tgz", - "integrity": "sha512-bQNB/+NdW55jlQ5lPnNqF5J90Tq4SihLbAF7tzPBvGDJyoYmDgwLm4FN0ZB3Ot9i1v6vJY/1mgqZZTT9jbYc5w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.5.0.tgz", + "integrity": "sha512-suqz9bzuhBQlf7u+bZd1bJLPzhXpk12w6AjQ9BTPTiFwexVZgUKViG1KNLyfFvW6tCUZZK0HmCCX7JAyM+JnCg==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^2.0.0", @@ -6962,9 +6968,9 @@ } }, "node_modules/rc-progress": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-3.5.1.tgz", - "integrity": "sha512-V6Amx6SbLRwPin/oD+k1vbPrO8+9Qf8zW1T8A7o83HdNafEVvAxPV5YsgtKFP+Ud5HghLj33zKOcEHrcrUGkfw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.6", @@ -6976,9 +6982,9 @@ } }, "node_modules/rc-rate": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.12.0.tgz", - "integrity": "sha512-g092v5iZCdVzbjdn28FzvWebK2IutoVoiTeqoLTj9WM7SjA/gOJIw5/JFZMRyJYYVe1jLAU2UhAfstIpCNRozg==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.0.tgz", + "integrity": "sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.5", @@ -7023,12 +7029,12 @@ } }, "node_modules/rc-select": { - "version": "14.13.0", - "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.13.0.tgz", - "integrity": "sha512-ew34FsaqHokK4dxVrcIxSYrgWJ2XJYlkk32eiOIiEo3GkHUExdCzmozMYaUc2P67c5QJRUvvY0uqCs3QG67h5A==", + "version": "14.14.0", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.14.0.tgz", + "integrity": "sha512-Uo2wulrjoPPRLCPd7zlK4ZFVJxlTN//yp1xWP/U+TUOQCyXrT+Duvq/Si5OzVcmQyWAUSbsplc2OwNNhvbOeKQ==", "dependencies": { "@babel/runtime": "^7.10.1", - "@rc-component/trigger": "^2.0.0", + "@rc-component/trigger": "^2.1.1", "classnames": "2.x", "rc-motion": "^2.0.1", "rc-overflow": "^1.3.1", @@ -7044,13 +7050,13 @@ } }, "node_modules/rc-slider": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.5.0.tgz", - "integrity": "sha512-xiYght50cvoODZYI43v3Ylsqiw14+D7ELsgzR40boDZaya1HFa1Etnv9MDkQE8X/UrXAffwv2AcNAhslgYuDTw==", + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.6.2.tgz", + "integrity": "sha512-FjkoFjyvUQWcBo1F3RgSglky3ar0+qHLM41PlFVYB4Bj3RD8E/Mv7kqMouLFBU+3aFglMzzctAIWRwajEuueSw==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.5", - "rc-util": "^5.27.0" + "rc-util": "^5.36.0" }, "engines": { "node": ">=8.x" @@ -7092,16 +7098,16 @@ } }, "node_modules/rc-table": { - "version": "7.42.0", - "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.42.0.tgz", - "integrity": "sha512-GwHV9Zs3HvWxBkoXatO/IeKoElzy3Ojf3dcyw1Rj3cyQVb+ZHtexslKdyzsrKRPJ0mUa62BoX+ZAg3zgTEql8w==", + "version": "7.45.7", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.45.7.tgz", + "integrity": "sha512-wi9LetBL1t1csxyGkMB2p3mCiMt+NDexMlPbXHvQFmBBAsMxrgNSAPwUci2zDLUq9m8QdWc1Nh8suvrpy9mXrg==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/context": "^1.4.0", "classnames": "^2.2.5", "rc-resize-observer": "^1.1.0", "rc-util": "^5.37.0", - "rc-virtual-list": "^3.11.1" + "rc-virtual-list": "^3.14.2" }, "engines": { "node": ">=8.x" @@ -7112,14 +7118,14 @@ } }, "node_modules/rc-tabs": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-14.1.1.tgz", - "integrity": "sha512-5nOr9PVpJy2SWHTLgv1+kESDOb0tFzl0cYU9r9d8LfL0Wg9i/n1B558rmkxdQHgBwMqxmwoyPSAbQROxMQe8nw==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.1.1.tgz", + "integrity": "sha512-Tc7bJvpEdkWIVCUL7yQrMNBJY3j44NcyWS48jF/UKMXuUlzaXK+Z/pEL5LjGcTadtPvVmNqA40yv7hmr+tCOAw==", "dependencies": { "@babel/runtime": "^7.11.2", "classnames": "2.x", "rc-dropdown": "~4.2.0", - "rc-menu": "~9.13.0", + "rc-menu": "~9.14.0", "rc-motion": "^2.6.2", "rc-resize-observer": "^1.0.0", "rc-util": "^5.34.1" @@ -7133,13 +7139,13 @@ } }, "node_modules/rc-textarea": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.6.3.tgz", - "integrity": "sha512-8k7+8Y2GJ/cQLiClFMg8kUXOOdvcFQrnGeSchOvI2ZMIVvX5a3zQpLxoODL0HTrvU63fPkRmMuqaEcOF9dQemA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.7.0.tgz", + "integrity": "sha512-UxizYJkWkmxP3zofXgc487QiGyDmhhheDLLjIWbFtDmiru1ls30KpO8odDaPyqNUIy9ugj5djxTEuezIn6t3Jg==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.1", - "rc-input": "~1.4.0", + "rc-input": "~1.5.0", "rc-resize-observer": "^1.0.0", "rc-util": "^5.27.0" }, @@ -7163,9 +7169,9 @@ } }, "node_modules/rc-tree": { - "version": "5.8.5", - "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.8.5.tgz", - "integrity": "sha512-PRfcZtVDNkR7oh26RuNe1hpw11c1wfgzwmPFL0lnxGnYefe9lDAO6cg5wJKIAwyXFVt5zHgpjYmaz0CPy1ZtKg==", + "version": "5.8.8", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.8.8.tgz", + "integrity": "sha512-S+mCMWo91m5AJqjz3PdzKilGgbFm7fFJRFiTDOcoRbD7UfMOPnerXwMworiga0O2XIo383UoWuEfeHs1WOltag==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", @@ -7182,13 +7188,13 @@ } }, "node_modules/rc-tree-select": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.19.0.tgz", - "integrity": "sha512-f4l5EsmSGF3ggj76YTzKNPY9SnXfFaer7ZccTSGb3urUf54L+cCqyT+UsPr+S5TAr8mZSxJ7g3CgkCe+cVQ6sw==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.21.0.tgz", + "integrity": "sha512-w+9qEu6zh0G3wt9N/hzWNSnqYH1i9mH1Nqxo0caxLRRFXF5yZWYmpCDoDTMdQM1Y4z3Q5yj08qyrPH/d4AtumA==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", - "rc-select": "~14.13.0", + "rc-select": "~14.14.0", "rc-tree": "~5.8.1", "rc-util": "^5.16.1" }, @@ -7212,9 +7218,9 @@ } }, "node_modules/rc-util": { - "version": "5.39.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.39.1.tgz", - "integrity": "sha512-OW/ERynNDgNr4y0oiFmtes3rbEamXw7GHGbkbNd9iRr7kgT03T6fT0b9WpJ3mbxKhyOcAHnGcIoh5u/cjrC2OQ==", + "version": "5.43.0", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.43.0.tgz", + "integrity": "sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw==", "dependencies": { "@babel/runtime": "^7.18.3", "react-is": "^18.2.0" @@ -7230,9 +7236,9 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/rc-virtual-list": { - "version": "3.11.4", - "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.11.4.tgz", - "integrity": "sha512-NbBi0fvyIu26gP69nQBiWgUMTPX3mr4FcuBQiVqagU0BnuX8WQkiivnMs105JROeuUIFczLrlgUhLQwTWV1XDA==", + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.14.3.tgz", + "integrity": "sha512-6+6wiEhdqakNBnbRJymgMlh+90qpkgqherTRo1l1cX7mK6F9hWsazPczmP0lA+64yhC9/t+M9Dh5pjvDWimn8A==", "dependencies": { "@babel/runtime": "^7.20.0", "classnames": "^2.2.6", @@ -8412,9 +8418,9 @@ } }, "node_modules/stylis": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", - "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" }, "node_modules/supports-color": { "version": "7.2.0", diff --git a/src/js/components/Beacon/BeaconQueryUi.tsx b/src/js/components/Beacon/BeaconQueryUi.tsx index f70944a4..282e09cc 100644 --- a/src/js/components/Beacon/BeaconQueryUi.tsx +++ b/src/js/components/Beacon/BeaconQueryUi.tsx @@ -19,6 +19,7 @@ import { } from '@/constants/beaconConstants'; import { BOX_SHADOW } from '@/constants/overviewConstants'; +import Loader from '@/components/Loader'; const { Text, Title } = Typography; // TODOs // example searches, either hardcoded or configurable @@ -236,7 +237,9 @@ const BeaconQueryUi = () => { const metadataInstructions = {td(METADATA_INSTRUCTIONS)}; const isFetchingBeaconQuery = useAppSelector((state) => state.beaconQuery.isFetchingQueryResponse); - return ( + return isFetchingBeaconConfig ? ( + + ) : (
( + + + + + + + + +); + +export default BeaconSvg; diff --git a/src/js/components/BentoAppRouter.tsx b/src/js/components/BentoAppRouter.tsx new file mode 100644 index 00000000..7e722240 --- /dev/null +++ b/src/js/components/BentoAppRouter.tsx @@ -0,0 +1,59 @@ +import React, { useEffect } from 'react'; +import { Routes, Route } from 'react-router-dom'; +import { useAutoAuthenticate, useIsAuthenticated } from 'bento-auth-js'; +import { useAppDispatch } from '@/hooks'; + +import { makeGetConfigRequest, makeGetServiceInfoRequest } from '@/features/config/config.store'; +import { makeGetAboutRequest } from '@/features/content/content.store'; +import { makeGetDataRequestThunk } from '@/features/data/data.store'; +import { makeGetKatsuPublic, makeGetSearchFields } from '@/features/search/query.store'; +import { makeGetProvenanceRequest } from '@/features/provenance/provenance.store'; +import { getBeaconConfig } from '@/features/beacon/beaconConfig.store'; +import { fetchGohanData, fetchKatsuData } from '@/features/ingestion/lastIngestion.store'; +import { makeGetDataTypes } from '@/features/dataTypes/dataTypes.store'; + +import PublicOverview from './Overview/PublicOverview'; +import Search from './Search/Search'; +import ProvenanceTab from './Provenance/ProvenanceTab'; +import BeaconQueryUi from './Beacon/BeaconQueryUi'; +import { BentoRoute } from '@/types/routes'; +import Loader from '@/components/Loader'; + +const BentoAppRouter = () => { + const dispatch = useAppDispatch(); + + const { isAutoAuthenticating } = useAutoAuthenticate(); + const isAuthenticated = useIsAuthenticated(); + + useEffect(() => { + dispatch(makeGetConfigRequest()).then(() => dispatch(getBeaconConfig())); + dispatch(makeGetAboutRequest()); + dispatch(makeGetDataRequestThunk()); + dispatch(makeGetSearchFields()); + dispatch(makeGetProvenanceRequest()); + dispatch(makeGetKatsuPublic()); + dispatch(fetchKatsuData()); + dispatch(fetchGohanData()); + dispatch(makeGetServiceInfoRequest()); + //TODO: Dispatch makeGetDataTypes to get the data types from service-registry + if (isAuthenticated) { + dispatch(makeGetDataTypes()); + } + }, [isAuthenticated]); + + if (isAutoAuthenticating) { + return ; + } + + return ( + + } /> + } /> + } /> + } /> + } /> + + ); +}; + +export default BentoAppRouter; diff --git a/src/js/components/Loader.tsx b/src/js/components/Loader.tsx index 09937875..cbbb2c23 100644 --- a/src/js/components/Loader.tsx +++ b/src/js/components/Loader.tsx @@ -4,7 +4,7 @@ import React from 'react'; const Loader = () => { return ( - + } /> diff --git a/src/js/components/Overview/ChartCard.tsx b/src/js/components/Overview/ChartCard.tsx index 63fa4cca..10bd74f9 100644 --- a/src/js/components/Overview/ChartCard.tsx +++ b/src/js/components/Overview/ChartCard.tsx @@ -1,14 +1,20 @@ -import React, { memo } from 'react'; -import Chart from './Chart'; +import React, { memo, useRef } from 'react'; import { Card, Button, Tooltip, Space, Typography, Row } from 'antd'; import { CloseOutlined, TeamOutlined } from '@ant-design/icons'; +import Chart from './Chart'; import CustomEmpty from '../Util/CustomEmpty'; import { CHART_HEIGHT, BOX_SHADOW } from '@/constants/overviewConstants'; -import { useTranslationCustom, useTranslationDefault } from '@/hooks'; +import { useElementWidth, useTranslationCustom, useTranslationDefault } from '@/hooks'; import { ChartDataField } from '@/types/data'; -const CARD_STYLE = { width: '100%', height: '415px', borderRadius: '11px', ...BOX_SHADOW }; -const ROW_EMPTY_STYLE = { height: `${CHART_HEIGHT}px` }; +const CARD_STYLE: React.CSSProperties = { height: '415px', borderRadius: '11px', ...BOX_SHADOW }; +const ROW_EMPTY_STYLE: React.CSSProperties = { height: `${CHART_HEIGHT}px` }; + +interface TitleComponentProps { + title: string; + description: string; + smallEllipsis: boolean; +} const TitleComponent: React.FC = ({ title, description, smallEllipsis }) => ( @@ -23,15 +29,11 @@ const TitleComponent: React.FC = ({ title, description, sma ); -interface TitleComponentProps { - title: string; - description: string; - smallEllipsis: boolean; -} - -const ChartCard = memo(({ section, chart, onRemoveChart, width }: ChartCardProps) => { +const ChartCard: React.FC = memo(({ section, chart, onRemoveChart }) => { const t = useTranslationCustom(); const td = useTranslationDefault(); + const containerRef = useRef(null); + const width = useElementWidth(containerRef, chart.width); const { data, @@ -71,9 +73,8 @@ const ChartCard = memo(({ section, chart, onRemoveChart, width }: ChartCardProps ); }); - // We add a key to the chart which includes width to force a re-render if width changes. return ( -
+
} style={CARD_STYLE} @@ -81,13 +82,7 @@ const ChartCard = memo(({ section, chart, onRemoveChart, width }: ChartCardProps extra={{ed}} > {data.filter((e) => !(e.x === 'missing')).length !== 0 ? ( - + ) : ( @@ -104,7 +99,6 @@ export interface ChartCardProps { section: string; chart: ChartDataField; onRemoveChart: (arg: { section: string; id: string }) => void; - width: number; } export default ChartCard; diff --git a/src/js/components/Overview/OverviewDisplayData.tsx b/src/js/components/Overview/OverviewDisplayData.tsx index 3b1f4daf..526c2da4 100644 --- a/src/js/components/Overview/OverviewDisplayData.tsx +++ b/src/js/components/Overview/OverviewDisplayData.tsx @@ -1,46 +1,19 @@ -import React, { CSSProperties, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { disableChart } from '@/features/data/data.store'; import { ChartDataField } from '@/types/data'; import ChartCard from './ChartCard'; - -const CHART_GUTTER = 16; - -const getColumnCount = (width: number): number => { - if (width < 880) { - return 1; - } else if (width < 1420) { - return 2; - } else return 3; -}; - -// Keep these quantized rather than a function of width for two reasons: -// - makes design more predictable -// - we don't need to re-render children for all width changes, just for some -const getFrameWidth = (width: number): number => { - if (width < 820) { - return 440; - } else if (width < 1060) { - return 780; - } else if (width < 1420) { - return 960; - } else { - return 1325; - } -}; +import { CHART_WIDTH, GRID_GAP } from '@/constants/overviewConstants'; const OverviewDisplayData = ({ section, allCharts }: OverviewDisplayDataProps) => { const dispatch = useDispatch(); - const { width } = useWindowSize(); - const columnCount = getColumnCount(width); - const frameWidth = getFrameWidth(width); - - const containerStyle = useMemo( - () => ({ width: frameWidth, display: 'flex', flexWrap: 'wrap', gap: CHART_GUTTER }), - [frameWidth] - ); + const containerStyle = { + display: 'grid', + gap: `${GRID_GAP}px`, + gridTemplateColumns: `repeat(auto-fit, ${CHART_WIDTH}px)`, + }; const displayedCharts = useMemo(() => allCharts.filter((e) => e.isDisplayed), [allCharts]); @@ -51,41 +24,13 @@ const OverviewDisplayData = ({ section, allCharts }: OverviewDisplayDataProps) = [dispatch] ); - const renderItem = useCallback( - (chart: ChartDataField) => { - const columnWidth = Math.min(chart.width, columnCount); - const pixelWidth = (columnWidth / columnCount) * (frameWidth - CHART_GUTTER * (columnCount - columnWidth)); - return ( - - ); - }, - [section, onRemoveChart, width] - ); + const renderItem = (chart: ChartDataField) => { + return ; + }; return
{displayedCharts.map(renderItem)}
; }; -const useWindowSize = () => { - const [windowSize, setWindowSize] = useState({ - width: window.innerWidth, - height: window.innerHeight, - }); - - useEffect(() => { - const handleResize = () => { - setWindowSize({ - width: window.innerWidth, - height: window.innerHeight, - }); - }; - window.addEventListener('resize', handleResize); - handleResize(); - return () => window.removeEventListener('resize', handleResize); - }, []); - - return windowSize; -}; - export interface OverviewDisplayDataProps { section: string; allCharts: ChartDataField[]; diff --git a/src/js/components/Overview/OverviewSection.tsx b/src/js/components/Overview/OverviewSection.tsx index aa364952..d6a5ccb9 100644 --- a/src/js/components/Overview/OverviewSection.tsx +++ b/src/js/components/Overview/OverviewSection.tsx @@ -9,7 +9,7 @@ const OverviewSection = ({ title, chartData }: { title: string; chartData: Chart const t = useTranslationCustom(); return ( - + {t(title)} diff --git a/src/js/components/Overview/PublicOverview.tsx b/src/js/components/Overview/PublicOverview.tsx index 43504b13..55028765 100644 --- a/src/js/components/Overview/PublicOverview.tsx +++ b/src/js/components/Overview/PublicOverview.tsx @@ -10,26 +10,27 @@ import { BOX_SHADOW, LOCALSTORAGE_CHARTS_KEY } from '@/constants/overviewConstan import OverviewSection from './OverviewSection'; import ManageChartsDrawer from './Drawer/ManageChartsDrawer'; import Counts from './Counts'; +import LastIngestionInfo from './LastIngestion'; +import Loader from '@/components/Loader'; + import { useAppSelector } from '@/hooks'; import { useTranslation } from 'react-i18next'; -import LastIngestionInfo from './LastIngestion'; -const ABOUT_CARD_STYLE = { borderRadius: '11pX', ...BOX_SHADOW }; +const ABOUT_CARD_STYLE = { width: '100%', maxWidth: '1390px', borderRadius: '11pX', ...BOX_SHADOW }; const MANAGE_CHARTS_BUTTON_STYLE = { right: '5em', bottom: '1.5em', transform: 'scale(125%)' }; const PublicOverview = () => { - const { sections } = useAppSelector((state) => state.data); + const { i18n } = useTranslation(); const [drawerVisible, setDrawerVisible] = useState(false); - - const { isFetchingAbout, about } = useAppSelector((state) => state.content); - const [aboutContent, setAboutContent] = useState(''); - const { i18n } = useTranslation(); + const { isFetchingData: isFetchingOverviewData, sections } = useAppSelector((state) => state.data); + const { isFetchingAbout, about } = useAppSelector((state) => state.content); useEffect(() => { // Save sections to localStorage when they change + if (isFetchingOverviewData) return; saveToLocalStorage(sections); }, [sections]); @@ -48,38 +49,33 @@ const PublicOverview = () => { saveToLocalStorage(sections); }, [sections]); - return ( + return isFetchingOverviewData ? ( + + ) : ( <> -
- - - - {isFetchingAbout ? ( - - ) : ( -
- )} - - - - - - - - - - - {displayedSections.map(({ sectionTitle, charts }, i) => ( -
- -
- ))} - - -
-
- - {/* Drawer & Button */} + + {isFetchingAbout ? ( + + ) : ( +
+ )} + + + + + + + + + {displayedSections.map(({ sectionTitle, charts }, i) => ( +
+ +
+ ))} + + +
+ { const qp1Keys = Object.keys(qp1); @@ -95,9 +96,13 @@ const SEARCH_SECTION_STYLE = { maxWidth: 1200 }; const Search: React.FC = () => { const t = useTranslationCustom(); - const searchSections = useAppSelector((state) => state.query.querySections); + const { isFetchingFields: isFetchingSearchFields, querySections: searchSections } = useAppSelector( + (state) => state.query + ); - return ( + return isFetchingSearchFields ? ( + + ) : ( <> diff --git a/src/js/components/SiteHeader.tsx b/src/js/components/SiteHeader.tsx index 6755e7af..1534869a 100644 --- a/src/js/components/SiteHeader.tsx +++ b/src/js/components/SiteHeader.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Button, Layout, Row, Col, Typography, Space } from 'antd'; +import { Button, Flex, Layout, Typography, Space } from 'antd'; const { Header } = Layout; import { useTranslation } from 'react-i18next'; import { DEFAULT_TRANSLATION, LNG_CHANGE, LNGS_FULL_NAMES } from '@/constants/configConstants'; @@ -7,6 +7,8 @@ import { useAppSelector } from '@/hooks'; import { useNavigate, useLocation } from 'react-router-dom'; import { useIsAuthenticated, usePerformAuth, usePerformSignOut } from 'bento-auth-js'; import { CLIENT_NAME, PORTAL_URL, TRANSLATED } from '@/config'; +import { RiTranslate } from 'react-icons/ri'; +import { ExportOutlined, LinkOutlined, LoginOutlined, LogoutOutlined } from '@ant-design/icons'; const openPortalWindow = () => window.open(PORTAL_URL, '_blank'); @@ -16,11 +18,9 @@ const SiteHeader = () => { const location = useLocation(); const { isFetching: openIdConfigFetching } = useAppSelector((state) => state.openIdConfiguration); - const { isHandingOffCodeForToken } = useAppSelector((state) => state.auth); const isAuthenticated = useIsAuthenticated(); - const performSignOut = usePerformSignOut(); const performSignIn = usePerformAuth(); @@ -32,44 +32,52 @@ const SiteHeader = () => { navigate(path, { replace: true }); }; - // noinspection HtmlUnknownTarget return ( -
- - - - - logo - - - {CLIENT_NAME} - - - - - - {TRANSLATED && ( - - )} - + )} + + {isAuthenticated ? ( + + ) : ( + - {isAuthenticated ? ( - - ) : ( - // - )} - - - + )} + +
); }; diff --git a/src/js/components/SitePageLoading.tsx b/src/js/components/SitePageLoading.tsx deleted file mode 100644 index 23953c03..00000000 --- a/src/js/components/SitePageLoading.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import { Skeleton } from 'antd'; - -const SitePageLoading = () => { - return ( - <> -
- -
- - ); -}; - -export default SitePageLoading; diff --git a/src/js/components/SiteSider.tsx b/src/js/components/SiteSider.tsx new file mode 100644 index 00000000..76f91679 --- /dev/null +++ b/src/js/components/SiteSider.tsx @@ -0,0 +1,89 @@ +import React, { useMemo, useCallback } from 'react'; +import { useNavigate, useLocation } from 'react-router-dom'; + +import { Layout, Menu } from 'antd'; +import type { MenuProps, SiderProps } from 'antd'; +import Icon, { PieChartOutlined, SearchOutlined, SolutionOutlined } from '@ant-design/icons'; + +import BeaconSvg from '@/components/Beacon/BeaconSvg'; +import { useAppSelector, useTranslationDefault } from '@/hooks'; +import { buildQueryParamsUrl } from '@/utils/search'; +import { getCurrentPage } from '@/utils/router'; +import { BentoRoute } from '@/types/routes'; + +const { Sider } = Layout; + +type CustomIconComponentProps = React.ComponentProps; +type MenuItem = Required['items'][number]; +type OnClick = MenuProps['onClick']; + +const BeaconLogo: React.FC> = (props) => ; + +const SiteSider: React.FC<{ + collapsed: boolean; + setCollapsed: SiderProps['onCollapse']; +}> = ({ collapsed, setCollapsed }) => { + const navigate = useNavigate(); + const location = useLocation(); + const td = useTranslationDefault(); + const queryParams = useAppSelector((state) => state.query.queryParams); + const currentPage = getCurrentPage(); + + const handleMenuClick: OnClick = useCallback( + ({ key }: { key: string }) => { + const currentPath = location.pathname.split('/'); + const currentLang = currentPath[1]; + const newPath = `/${currentLang}/${key === BentoRoute.Overview ? '' : key}`; + navigate(key === BentoRoute.Search ? buildQueryParamsUrl(newPath, queryParams) : newPath); + }, + [navigate, queryParams, location.pathname] + ); + + const createMenuItem = useCallback( + (label: string, key: string, icon?: React.ReactNode, children?: MenuItem[]): MenuItem => ({ + key, + icon, + children, + label: td(label), + }), + [td] + ); + + const menuItems: MenuItem[] = useMemo( + () => [ + createMenuItem('Overview', BentoRoute.Overview, ), + createMenuItem('Search', BentoRoute.Search, ), + createMenuItem('Beacon', BentoRoute.Beacon, ), + createMenuItem('Provenance', BentoRoute.Provenance, ), + ], + [createMenuItem] + ); + + return ( + + + + ); +}; + +export default SiteSider; diff --git a/src/js/components/TabbedDashboard.tsx b/src/js/components/TabbedDashboard.tsx deleted file mode 100644 index c7811625..00000000 --- a/src/js/components/TabbedDashboard.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { useNavigate, useLocation, useParams } from 'react-router-dom'; -import { Tabs, Typography } from 'antd'; -import { useAutoAuthenticate, useIsAuthenticated } from 'bento-auth-js'; - -const { Title } = Typography; - -import { makeGetConfigRequest, makeGetServiceInfoRequest } from '@/features/config/config.store'; -import { makeGetAboutRequest } from '@/features/content/content.store'; -import { makeGetDataRequestThunk } from '@/features/data/data.store'; -import { makeGetSearchFields } from '@/features/search/query.store'; -import { makeGetProvenanceRequest } from '@/features/provenance/provenance.store'; -import { getBeaconConfig } from '@/features/beacon/beaconConfig.store'; -import { fetchGohanData, fetchKatsuData } from '@/features/ingestion/lastIngestion.store'; - -import Loader from './Loader'; -import PublicOverview from './Overview/PublicOverview'; -import Search from './Search/Search'; -import ProvenanceTab from './Provenance/ProvenanceTab'; -import BeaconQueryUi from './Beacon/BeaconQueryUi'; -import { useAppDispatch, useAppSelector, useTranslationDefault } from '@/hooks'; -import { buildQueryParamsUrl } from '@/utils/search'; -import { makeGetDataTypes } from '@/features/dataTypes/dataTypes.store'; -import { BEACON_UI_ENABLED } from '@/config'; -import SitePageLoading from './SitePageLoading'; - -const TabbedDashboard = () => { - const dispatch = useAppDispatch(); - const td = useTranslationDefault(); - const navigate = useNavigate(); - const location = useLocation(); - const { page } = useParams<{ page?: string }>(); - - const { isAutoAuthenticating } = useAutoAuthenticate(); - const isAuthenticated = useIsAuthenticated(); - - useEffect(() => { - dispatch(makeGetConfigRequest()).then(() => dispatch(getBeaconConfig())); - dispatch(makeGetAboutRequest()); - dispatch(makeGetDataRequestThunk()); - dispatch(makeGetSearchFields()); - dispatch(makeGetProvenanceRequest()); - dispatch(fetchKatsuData()); - dispatch(fetchGohanData()); - dispatch(makeGetServiceInfoRequest()); - //TODO: Dispatch makeGetDataTypes to get the data types from service-registry - if (isAuthenticated) { - dispatch(makeGetDataTypes()); - } - }, [isAuthenticated]); - - const isFetchingOverviewData = useAppSelector((state) => state.data.isFetchingData); - const isFetchingSearchFields = useAppSelector((state) => state.query.isFetchingFields); - const queryParams = useAppSelector((state) => state.query.queryParams); - const isFetchingBeaconConfig = useAppSelector((state) => state.beaconConfig?.isFetchingBeaconConfig); - - const onChange = useCallback( - (key: string) => { - const currentPath = location.pathname; - const currentPathParts = currentPath.split('/'); - const currentLang = currentPathParts[1]; - const newPath = `/${currentLang}/${key === 'overview' ? '' : key}`; - // If we're going to the search page, insert query params into the URL pulled from the Redux state. - // This is important to keep the URL updated if we've searched something, navigated away, and now - // are returning to the search page. - navigate(key === 'search' ? buildQueryParamsUrl(newPath, queryParams) : newPath); - }, - [location, navigate, queryParams] - ); - - const TabTitle = ({ title }: { title: string }) => ( - - {title} - - ); - - const tabPanes = [ - { - title: 'Overview', - content: , - loading: isFetchingOverviewData, - active: true, - key: 'overview', - }, - { - title: 'Search', - content: , - loading: isFetchingSearchFields, - active: true, - key: 'search', - }, - { - title: 'Beacon', - content: , - loading: isFetchingBeaconConfig, - active: BEACON_UI_ENABLED, - key: 'beacon', - }, - { - title: 'Provenance', - content: , - loading: false, - active: true, - key: 'provenance', - }, - ]; - - const mappedTabPanes = tabPanes - .filter((t) => t.active) - .map(({ title, content, loading, key }) => ({ - label: , - children: loading ? : content, - key, - })); - - const getTabKey = (page: string | undefined) => { - if (page && mappedTabPanes.map((t) => t.key).includes(page)) { - return page; - } - return 'overview'; - }; - - if (isAutoAuthenticating) { - return ; - } - - return ; -}; - -export default TabbedDashboard; diff --git a/src/js/constants/overviewConstants.ts b/src/js/constants/overviewConstants.ts index 36d97d3e..0aaabb8f 100644 --- a/src/js/constants/overviewConstants.ts +++ b/src/js/constants/overviewConstants.ts @@ -10,6 +10,9 @@ export const DEFAULT_CHART_WIDTH = 1; export const BOX_SHADOW = { boxShadow: '0 2px 10px rgba(0,0,0,0.05)' }; +export const CHART_WIDTH = 450; +export const GRID_GAP = 20; + const NEW_CHART_COLORS: HexColor[] = ['#90BE6D', '#F8961E', '#F3722C', '#2D9CDB', '#F94144', '#F9C74F']; const BAR_CHART_FILL: HexColor = '#2D9CDB'; const CHART_MISSING_FILL: HexColor = '#bbbbbb'; diff --git a/src/js/features/search/query.store.ts b/src/js/features/search/query.store.ts index 0f1244bc..b3db5387 100644 --- a/src/js/features/search/query.store.ts +++ b/src/js/features/search/query.store.ts @@ -22,7 +22,7 @@ type QueryState = { }; const initialState: QueryState = { - isFetchingFields: true, + isFetchingFields: false, isFetchingData: false, attemptedFetch: false, message: '', diff --git a/src/js/hooks.ts b/src/js/hooks.ts index 4ac81fda..761bf64b 100644 --- a/src/js/hooks.ts +++ b/src/js/hooks.ts @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { RESOURCE_EVERYTHING, Resource, queryData, useHasResourcePermission } from 'bento-auth-js'; @@ -7,6 +7,7 @@ import type { RootState, AppDispatch } from '@/store'; import { DEFAULT_TRANSLATION, NON_DEFAULT_TRANSLATION } from '@/constants/configConstants'; import { NamespaceTranslationFunction } from '@/types/translation'; import { setMaxQueryParametersRequired } from '@/features/config//config.store'; +import { CHART_WIDTH, GRID_GAP } from '@/constants/overviewConstants'; // Use throughout your app instead of plain `useDispatch` and `useSelector` // refer to https://react-redux.js.org/using-react-redux/usage-with-typescript#define-typed-hooks for more info @@ -50,3 +51,52 @@ export const useBeaconWithAuthIfAllowed = () => { } }, [dispatch, hasPermission]); }; + +// ################### OVERFLOW HOOKS ################### +function isElementOutOfView(element: HTMLElement): boolean { + const rect = element.getBoundingClientRect(); + return rect.right + 64 > window.innerWidth; +} + +function isSpaceAvailable(element: HTMLElement): boolean { + const rect = element.getBoundingClientRect(); + return rect.right + CHART_WIDTH + GRID_GAP + 64 < window.innerWidth; +} + +export const useElementWidth = (ref: React.RefObject, initialWidth: number) => { + const [width, setWidth] = useState(initialWidth); + + const adjustWidth = useCallback(() => { + if (ref.current) { + if (initialWidth < width) { + setWidth(initialWidth); + } else if (isElementOutOfView(ref.current)) { + setWidth((prevWidth) => Math.max(prevWidth - 1, 1)); + } else if (width < initialWidth && isSpaceAvailable(ref.current)) { + setWidth((prevWidth) => Math.min(prevWidth + 1, initialWidth)); + } + } + }, [ref, width, initialWidth]); + + useEffect(() => { + adjustWidth(); + }, [adjustWidth, initialWidth]); + + useEffect(() => { + const handleResize = debounceAdjustWidth(adjustWidth, 200); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, [adjustWidth]); + + return width; +}; + +function debounceAdjustWidth(callback: () => void, delay: number) { + let timeoutId: NodeJS.Timeout; + return () => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + callback(); + }, delay); + }; +} diff --git a/src/js/index.tsx b/src/js/index.tsx index 47156baa..62d7d0d7 100644 --- a/src/js/index.tsx +++ b/src/js/index.tsx @@ -12,12 +12,13 @@ import { NEW_BENTO_PUBLIC_THEME } from '@/constants/overviewConstants'; import { DEFAULT_TRANSLATION, SUPPORTED_LNGS } from '@/constants/configConstants'; // Component imports -import { Button, Layout, Modal, message } from 'antd'; +import { Button, ConfigProvider, Layout, Modal, message } from 'antd'; import { ChartConfigProvider } from 'bento-charts'; -import TabbedDashboard from '@/components/TabbedDashboard'; import SiteHeader from '@/components/SiteHeader'; import SiteFooter from '@/components/SiteFooter'; -import SitePageLoading from '@/components/SitePageLoading'; +import SiteSider from '@/components/SiteSider'; +import Loader from '@/components/Loader'; +import BentoAppRouter from '@/components/BentoAppRouter'; // Hooks and utilities imports import { @@ -52,6 +53,7 @@ const createSessionWorker = () => new Worker(new URL('./workers/tokenRefresh.ts' const App = () => { const navigate = useNavigate(); + const [collapsed, setCollapsed] = useState(false); // TRANSLATION const { lang } = useParams<{ lang?: string }>(); @@ -98,7 +100,7 @@ const App = () => { useBeaconWithAuthIfAllowed(); return ( - <> + { @@ -116,17 +118,28 @@ const App = () => { - - }> - - } /> - } /> - - - - + + + + + }> + + } /> + } /> + + + + + + - + ); }; diff --git a/src/js/types/routes.ts b/src/js/types/routes.ts new file mode 100644 index 00000000..a6887773 --- /dev/null +++ b/src/js/types/routes.ts @@ -0,0 +1,6 @@ +export enum BentoRoute { + Overview = 'overview', + Search = 'search', + Beacon = 'beacon', + Provenance = 'provenance', +} diff --git a/src/js/utils/router.ts b/src/js/utils/router.ts new file mode 100644 index 00000000..534c8a6d --- /dev/null +++ b/src/js/utils/router.ts @@ -0,0 +1,11 @@ +import { BentoRoute } from '@/types/routes'; + +export const getCurrentPage = (): BentoRoute => { + const pathArray = window.location.pathname.split('/'); + const validPages = Object.values(BentoRoute); + if (pathArray.length > 2 && validPages.includes(pathArray[2] as BentoRoute)) { + return pathArray[2] as BentoRoute; + } else { + return BentoRoute.Overview; + } +}; diff --git a/src/public/assets/branding.png b/src/public/assets/branding.png index dced5abb..4d99f910 100644 Binary files a/src/public/assets/branding.png and b/src/public/assets/branding.png differ diff --git a/src/public/assets/branding_color.png b/src/public/assets/branding_color.png new file mode 100644 index 00000000..dced5abb Binary files /dev/null and b/src/public/assets/branding_color.png differ diff --git a/src/public/config.js b/src/public/config.js index 4bc364b7..8e98a7ef 100644 --- a/src/public/config.js +++ b/src/public/config.js @@ -1,14 +1,14 @@ // Global to include as a config BENTO_PUBLIC_CONFIG = { - // GENERAL - CLIENT_NAME: null, - PORTAL_URL: null, - TRANSLATED: null, - BEACON_URL: null, - BEACON_UI_ENABLED: null, - // Authentication - PUBLIC_URL: null, - CLIENT_ID: null, - OPENID_CONFIG_URL: null, -} + // GENERAL + CLIENT_NAME: null, + PORTAL_URL: null, + TRANSLATED: null, + BEACON_URL: null, + BEACON_UI_ENABLED: null, + // Authentication + PUBLIC_URL: null, + CLIENT_ID: null, + OPENID_CONFIG_URL: null, +}; diff --git a/src/styles.css b/src/styles.css index 17f80ced..994e396b 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,7 +1,7 @@ html, body { min-height: 100%; - background-color: #eff2f5; + background-color: #f5f5f5; margin: 0; } @@ -11,9 +11,14 @@ body { margin: auto; } -.floating-search { - position: fixed !important; - right: 5em !important; - bottom: 3em !important; - transform: scale(125%) !important; +.header-button { + color: lightgray; +} + +.header-button:hover { + color: white !important; +} + +.ant-layout-sider-trigger { + border-right: 1px solid #f0f0f0; } diff --git a/webpack.config.js b/webpack.config.js index ce88930c..1420bfbe 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -59,7 +59,7 @@ const config = { BENTO_PUBLIC_URL: null, CLIENT_ID: null, OPENID_CONFIG_URL: null, - }) + }), ], optimization: { runtimeChunk: 'single',