This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
The following commit(s) were added to refs/heads/master by this push:
new c3aed3ae7 User page UI and code refinements (#416)
c3aed3ae7 is described below
commit c3aed3ae7ce70cec3760762fda9881004c45acaa
Author: Ganning Xu <[email protected]>
AuthorDate: Fri Feb 7 11:31:40 2025 -0500
User page UI and code refinements (#416)
* UI of user's page
* refactor components
* remove unused imports
---
custos-portal/package-lock.json | 561 ++++++++++++++++++++-
custos-portal/src/App.tsx | 45 +-
.../src/components/Groups/GroupSettings.tsx | 49 +-
custos-portal/src/components/LeftRightLayout.tsx | 16 +
custos-portal/src/components/NavContainer.tsx | 358 +++++++------
custos-portal/src/components/StackedBorderBox.tsx | 23 +
.../src/components/Users/UserSettings.tsx | 196 +++++++
custos-portal/src/components/Users/index.tsx | 117 +++++
custos-portal/src/index.tsx | 52 +-
custos-portal/src/interfaces/Users.tsx | 6 +
custos-portal/src/lib/constants.ts | 12 +-
11 files changed, 1179 insertions(+), 256 deletions(-)
diff --git a/custos-portal/package-lock.json b/custos-portal/package-lock.json
index cc346a33e..efbe98684 100644
--- a/custos-portal/package-lock.json
+++ b/custos-portal/package-lock.json
@@ -1908,6 +1908,20 @@
"@zag-js/dom-query": "0.16.0"
}
},
+ "@zeit/schemas": {
+ "version": "2.36.0",
+ "resolved":
"https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz",
+ "integrity":
"sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg=="
+ },
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity":
"sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
"acorn": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
@@ -1932,11 +1946,30 @@
"uri-js": "^4.2.2"
}
},
+ "ansi-align": {
+ "version": "3.0.1",
+ "resolved":
"https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity":
"sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "requires": {
+ "string-width": "^4.1.0"
+ },
+ "dependencies": {
+ "string-width": {
+ "version": "4.2.3",
+ "resolved":
"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity":
"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ }
+ }
+ },
"ansi-regex": {
"version": "5.0.1",
"resolved":
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity":
"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true
+ "integrity":
"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "3.2.1",
@@ -1946,6 +1979,16 @@
"color-convert": "^1.9.0"
}
},
+ "arch": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+ "integrity":
"sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ=="
+ },
+ "arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity":
"sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
+ },
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2019,14 +2062,34 @@
"balanced-match": {
"version": "1.0.2",
"resolved":
"https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity":
"sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "integrity":
"sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "boxen": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz",
+ "integrity":
"sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==",
+ "requires": {
+ "ansi-align": "^3.0.1",
+ "camelcase": "^7.0.0",
+ "chalk": "^5.0.1",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^5.1.2",
+ "type-fest": "^2.13.0",
+ "widest-line": "^4.0.1",
+ "wrap-ansi": "^8.0.1"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity":
"sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="
+ }
+ }
},
"brace-expansion": {
"version": "1.1.11",
"resolved":
"https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity":
"sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -2053,11 +2116,21 @@
"update-browserslist-db": "^1.1.0"
}
},
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity":
"sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw=="
+ },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity":
"sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
+ "camelcase": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
+ "integrity":
"sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="
+ },
"caniuse-lite": {
"version": "1.0.30001651",
"resolved":
"https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
@@ -2074,6 +2147,74 @@
"supports-color": "^5.3.0"
}
},
+ "chalk-template": {
+ "version": "0.4.0",
+ "resolved":
"https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
+ "integrity":
"sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
+ "requires": {
+ "chalk": "^4.1.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved":
"https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity":
"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity":
"sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved":
"https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity":
"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved":
"https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity":
"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved":
"https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity":
"sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved":
"https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity":
"sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "cli-boxes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+ "integrity":
"sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="
+ },
+ "clipboardy": {
+ "version": "3.0.0",
+ "resolved":
"https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
+ "integrity":
"sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==",
+ "requires": {
+ "arch": "^2.2.0",
+ "execa": "^5.1.1",
+ "is-wsl": "^2.2.0"
+ }
+ },
"color-convert": {
"version": "1.9.3",
"resolved":
"https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -2100,6 +2241,43 @@
"delayed-stream": "~1.0.0"
}
},
+ "compressible": {
+ "version": "2.0.18",
+ "resolved":
"https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity":
"sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.4",
+ "resolved":
"https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity":
"sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity":
"sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity":
"sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ }
+ }
+ },
"compute-scroll-into-view": {
"version": "3.0.3",
"resolved":
"https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.0.3.tgz",
@@ -2108,8 +2286,12 @@
"concat-map": {
"version": "0.0.1",
"resolved":
"https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity":
"sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true
+ "integrity":
"sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+ },
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved":
"https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity":
"sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA=="
},
"convert-source-map": {
"version": "2.0.0",
@@ -2141,7 +2323,6 @@
"version": "7.0.3",
"resolved":
"https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity":
"sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -2169,6 +2350,11 @@
"ms": "2.1.2"
}
},
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved":
"https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity":
"sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+ },
"deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -2199,12 +2385,22 @@
"path-type": "^4.0.0"
}
},
+ "eastasianwidth": {
+ "version": "0.2.0",
+ "resolved":
"https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity":
"sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+ },
"electron-to-chromium": {
"version": "1.5.8",
"resolved":
"https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.8.tgz",
"integrity":
"sha512-4Nx0gP2tPNBLTrFxBMHpkQbtn2hidPVr/+/FTtcCiBYTucqc70zRyVZiOLj17Ui3wTO7SQ1/N+hkHYzJjBzt6A==",
"dev": true
},
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved":
"https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity":
"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -2423,11 +2619,26 @@
"integrity":
"sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
+ "execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity":
"sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
"fast-deep-equal": {
"version": "3.1.3",
"resolved":
"https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity":
"sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
+ "integrity":
"sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-glob": {
"version": "3.3.2",
@@ -2592,6 +2803,11 @@
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
"integrity":
"sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="
},
+ "get-stream": {
+ "version": "6.0.1",
+ "resolved":
"https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity":
"sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+ },
"glob-parent": {
"version": "6.0.2",
"resolved":
"https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -2648,6 +2864,11 @@
"react-is": "^16.7.0"
}
},
+ "human-signals": {
+ "version": "2.1.0",
+ "resolved":
"https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity":
"sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
+ },
"ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2669,6 +2890,11 @@
"integrity":
"sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true
},
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity":
"sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ },
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -2690,12 +2916,22 @@
"hasown": "^2.0.2"
}
},
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity":
"sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
+ },
"is-extglob": {
"version": "2.1.1",
"resolved":
"https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity":
"sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true
},
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved":
"https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity":
"sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ },
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -2717,11 +2953,28 @@
"integrity":
"sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true
},
+ "is-port-reachable": {
+ "version": "4.0.0",
+ "resolved":
"https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz",
+ "integrity":
"sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig=="
+ },
+ "is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity":
"sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
+ },
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity":
"sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity":
"sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "integrity":
"sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"js-tokens": {
"version": "4.0.0",
@@ -2837,6 +3090,11 @@
"yallist": "^3.0.2"
}
},
+ "merge-stream": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity":
"sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ },
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -2866,15 +3124,24 @@
"mime-db": "1.52.0"
}
},
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity":
"sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+ },
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity":
"sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
+ "minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity":
"sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
+ },
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -2892,12 +3159,25 @@
"integrity":
"sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved":
"https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity":
"sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+ },
"node-releases": {
"version": "2.0.18",
"resolved":
"https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
"integrity":
"sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
"dev": true
},
+ "npm-run-path": {
+ "version": "4.0.1",
+ "resolved":
"https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity":
"sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "requires": {
+ "path-key": "^3.0.0"
+ }
+ },
"object-assign": {
"version": "4.1.1",
"resolved":
"https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -2911,6 +3191,19 @@
"jwt-decode": "^4.0.0"
}
},
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved":
"https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity":
"sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity":
"sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
"optionator": {
"version": "0.9.4",
"resolved":
"https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -2968,17 +3261,26 @@
"integrity":
"sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved":
"https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity":
"sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="
+ },
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity":
"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
+ "integrity":
"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-parse": {
"version": "1.0.7",
"resolved":
"https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity":
"sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
+ "path-to-regexp": {
+ "version": "3.3.0",
+ "resolved":
"https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz",
+ "integrity":
"sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw=="
+ },
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -3030,8 +3332,7 @@
"punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity":
"sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true
+ "integrity":
"sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
},
"queue-microtask": {
"version": "1.2.3",
@@ -3039,6 +3340,29 @@
"integrity":
"sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true
},
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved":
"https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity":
"sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A=="
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity":
"sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved":
"https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity":
"sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
+ }
+ }
+ },
"react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -3161,6 +3485,28 @@
"resolved":
"https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity":
"sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
+ "registry-auth-token": {
+ "version": "3.3.2",
+ "resolved":
"https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
+ "integrity":
"sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
+ "requires": {
+ "rc": "^1.1.6",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "registry-url": {
+ "version": "3.1.0",
+ "resolved":
"https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+ "integrity":
"sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
+ "requires": {
+ "rc": "^1.0.1"
+ }
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved":
"https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity":
"sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+ },
"resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -3217,6 +3563,11 @@
"queue-microtask": "^1.2.2"
}
},
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved":
"https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity":
"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
"scheduler": {
"version": "0.23.2",
"resolved":
"https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -3231,11 +3582,80 @@
"integrity":
"sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true
},
+ "serve": {
+ "version": "14.2.4",
+ "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz",
+ "integrity":
"sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==",
+ "requires": {
+ "@zeit/schemas": "2.36.0",
+ "ajv": "8.12.0",
+ "arg": "5.0.2",
+ "boxen": "7.0.0",
+ "chalk": "5.0.1",
+ "chalk-template": "0.4.0",
+ "clipboardy": "3.0.0",
+ "compression": "1.7.4",
+ "is-port-reachable": "4.0.0",
+ "serve-handler": "6.1.6",
+ "update-check": "1.5.4"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
+ "integrity":
"sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "chalk": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz",
+ "integrity":
"sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w=="
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved":
"https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity":
"sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ }
+ }
+ },
+ "serve-handler": {
+ "version": "6.1.6",
+ "resolved":
"https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz",
+ "integrity":
"sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==",
+ "requires": {
+ "bytes": "3.0.0",
+ "content-disposition": "0.5.2",
+ "mime-types": "2.1.18",
+ "minimatch": "3.1.2",
+ "path-is-inside": "1.0.2",
+ "path-to-regexp": "3.3.0",
+ "range-parser": "1.2.0"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.33.0",
+ "resolved":
"https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+ "integrity":
"sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
+ },
+ "mime-types": {
+ "version": "2.1.18",
+ "resolved":
"https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+ "integrity":
"sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+ "requires": {
+ "mime-db": "~1.33.0"
+ }
+ }
+ }
+ },
"shebang-command": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity":
"sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
@@ -3243,8 +3663,12 @@
"shebang-regex": {
"version": "3.0.0",
"resolved":
"https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity":
"sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
+ "integrity":
"sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
+ },
+ "signal-exit": {
+ "version": "3.0.7",
+ "resolved":
"https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity":
"sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"slash": {
"version": "3.0.0",
@@ -3263,15 +3687,49 @@
"integrity":
"sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true
},
+ "string-width": {
+ "version": "5.1.2",
+ "resolved":
"https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity":
"sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "requires": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.1.0",
+ "resolved":
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity":
"sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="
+ },
+ "emoji-regex": {
+ "version": "9.2.2",
+ "resolved":
"https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity":
"sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "resolved":
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity":
"sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
"strip-ansi": {
"version": "6.0.1",
"resolved":
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity":
"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
},
+ "strip-final-newline": {
+ "version": "2.0.0",
+ "resolved":
"https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity":
"sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
+ },
"strip-json-comments": {
"version": "3.1.1",
"resolved":
"https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -3346,6 +3804,11 @@
"prelude-ls": "^1.2.1"
}
},
+ "type-fest": {
+ "version": "2.19.0",
+ "resolved":
"https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity":
"sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="
+ },
"typescript": {
"version": "5.5.4",
"resolved":
"https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
@@ -3379,11 +3842,19 @@
"picocolors": "^1.0.1"
}
},
+ "update-check": {
+ "version": "1.5.4",
+ "resolved":
"https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz",
+ "integrity":
"sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==",
+ "requires": {
+ "registry-auth-token": "3.3.2",
+ "registry-url": "3.1.0"
+ }
+ },
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity":
"sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
"requires": {
"punycode": "^2.1.0"
}
@@ -3405,6 +3876,11 @@
"tslib": "^2.0.0"
}
},
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity":
"sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+ },
"vite": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz",
@@ -3421,17 +3897,54 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity":
"sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"requires": {
"isexe": "^2.0.0"
}
},
+ "widest-line": {
+ "version": "4.0.1",
+ "resolved":
"https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
+ "integrity":
"sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
+ "requires": {
+ "string-width": "^5.0.1"
+ }
+ },
"word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity":
"sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true
},
+ "wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity":
"sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "requires": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.1.0",
+ "resolved":
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity":
"sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="
+ },
+ "ansi-styles": {
+ "version": "6.2.1",
+ "resolved":
"https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity":
"sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "resolved":
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity":
"sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -3450,4 +3963,4 @@
"dev": true
}
}
-}
\ No newline at end of file
+}
diff --git a/custos-portal/src/App.tsx b/custos-portal/src/App.tsx
index da7b30496..71bf14dbd 100644
--- a/custos-portal/src/App.tsx
+++ b/custos-portal/src/App.tsx
@@ -17,34 +17,51 @@
* under the License.
*/
-import { Routes, Route, BrowserRouter } from 'react-router-dom';
-import { Heading } from '@chakra-ui/react';
-import { Groups } from './components/Groups';
-import { NavContainer } from './components/NavContainer';
-import { GroupDetails } from './components/Groups/GroupDetails';
-import { Login } from './components/Login';
-import ProtectedComponent from './components/ProtectedComponent';
+import { Routes, Route, BrowserRouter } from "react-router-dom";
+import { Heading } from "@chakra-ui/react";
+import { Groups } from "./components/Groups";
+import { NavContainer } from "./components/NavContainer";
+import { GroupDetails } from "./components/Groups/GroupDetails";
+import { Login } from "./components/Login";
+import { Users } from "./components/Users";
+import { UserSettings } from "./components/Users/UserSettings";
+import ProtectedComponent from "./components/ProtectedComponent";
function NotImplemented() {
return (
- <NavContainer activeTab='N/A'>
- <Heading size='lg' fontWeight={500}>
+ <NavContainer activeTab="N/A">
+ <Heading size="lg" fontWeight={500}>
Not Implemented
</Heading>
</NavContainer>
);
}
-
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Login />} />
- <Route path="/applications" element={<ProtectedComponent
Component={NotImplemented} />} />
- <Route path="/users" element={<ProtectedComponent
Component={NotImplemented} />} />
- <Route path="/groups/:id/:path" element={<ProtectedComponent
Component={GroupDetails} />} />
- <Route path="/groups" element={<ProtectedComponent
Component={Groups} />} />
+ <Route
+ path="/applications"
+ element={<ProtectedComponent Component={NotImplemented} />}
+ />
+ <Route
+ path="/users"
+ element={<ProtectedComponent Component={Users} />}
+ />
+ <Route
+ path="/users/:email"
+ element={<ProtectedComponent Component={UserSettings} />}
+ />
+ <Route
+ path="/groups/:id/:path"
+ element={<ProtectedComponent Component={GroupDetails} />}
+ />
+ <Route
+ path="/groups"
+ element={<ProtectedComponent Component={Groups} />}
+ />
</Routes>
</BrowserRouter>
);
diff --git a/custos-portal/src/components/Groups/GroupSettings.tsx
b/custos-portal/src/components/Groups/GroupSettings.tsx
index 00e6e3566..e8119941c 100644
--- a/custos-portal/src/components/Groups/GroupSettings.tsx
+++ b/custos-portal/src/components/Groups/GroupSettings.tsx
@@ -24,9 +24,7 @@ import {
Text,
FormLabel,
Input,
- SimpleGrid,
Stack,
- Divider,
Button,
Table,
Thead,
@@ -52,26 +50,13 @@ import { Group, Member } from "../../interfaces/Groups";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { TransferOwnershipModal } from "./TransferOwnershipModal";
+import { LeftRightLayout } from "../LeftRightLayout";
+import { StackedBorderBox } from "../StackedBorderBox";
interface GroupSettingsProps {
groupId: string | undefined;
}
-const LeftRightLayout = ({
- left,
- right,
-}: {
- left: React.ReactNode;
- right: React.ReactNode;
-}) => {
- return (
- <SimpleGrid columns={2} spacing={8}>
- <Box>{left}</Box>
- <Box>{right}</Box>
- </SimpleGrid>
- );
-};
-
export const GroupSettings = ({ groupId }: GroupSettingsProps) => {
const [name, setName] = React.useState("");
const [description, setDescription] = React.useState("");
@@ -131,7 +116,7 @@ export const GroupSettings = ({ groupId }:
GroupSettingsProps) => {
})();
}, []);
- const handleSaveChanges = async() => {
+ const handleSaveChanges = async () => {
console.log(name, description);
const resp = await customFetch(
@@ -140,11 +125,11 @@ export const GroupSettings = ({ groupId }:
GroupSettingsProps) => {
method: "PUT",
body: JSON.stringify({
name,
- description
+ description,
}),
headers: {
- "Content-Type": "application/json"
- }
+ "Content-Type": "application/json",
+ },
}
);
@@ -155,22 +140,18 @@ export const GroupSettings = ({ groupId }:
GroupSettingsProps) => {
navigate(0);
} else {
toast({
- title: 'Could not save group',
- status: 'error',
+ title: "Could not save group",
+ status: "error",
duration: 3000,
isClosable: true,
});
}
-
-
- }
+ };
if (!groupId) {
return;
}
-
-
return (
<>
<PageTitle size="md">Group Settings</PageTitle>
@@ -178,15 +159,7 @@ export const GroupSettings = ({ groupId }:
GroupSettingsProps) => {
Edit group membership, roles, and other information.
</Text>
- <Stack
- border="1px solid"
- borderColor="border.neutral.tertiary"
- rounded="xl"
- p={8}
- mt={8}
- divider={<Divider />}
- spacing={8}
- >
+ <StackedBorderBox>
<LeftRightLayout
left={<Text fontSize="lg">Basic Information</Text>}
right={
@@ -349,7 +322,7 @@ export const GroupSettings = ({ groupId }:
GroupSettingsProps) => {
</Flex>
}
/>
- </Stack>
+ </StackedBorderBox>
<Stack direction="row" mt={8} spacing={4}>
<ActionButton onClick={handleSaveChanges}>Save Changes</ActionButton>
diff --git a/custos-portal/src/components/LeftRightLayout.tsx
b/custos-portal/src/components/LeftRightLayout.tsx
new file mode 100644
index 000000000..a66603fd7
--- /dev/null
+++ b/custos-portal/src/components/LeftRightLayout.tsx
@@ -0,0 +1,16 @@
+import { SimpleGrid, Box } from "@chakra-ui/react";
+
+export const LeftRightLayout = ({
+ left,
+ right,
+}: {
+ left: React.ReactNode;
+ right: React.ReactNode;
+}) => {
+ return (
+ <SimpleGrid columns={2} spacing={8}>
+ <Box>{left}</Box>
+ <Box>{right}</Box>
+ </SimpleGrid>
+ );
+};
diff --git a/custos-portal/src/components/NavContainer.tsx
b/custos-portal/src/components/NavContainer.tsx
index 138127596..e800485e9 100644
--- a/custos-portal/src/components/NavContainer.tsx
+++ b/custos-portal/src/components/NavContainer.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import React, { useState, useEffect, memo } from 'react';
+import React, { useState, useEffect, memo } from "react";
import {
Grid,
GridItem,
@@ -34,14 +34,20 @@ import {
DrawerCloseButton,
DrawerBody,
useDisclosure,
- Spacer
-} from '@chakra-ui/react';
-import { Link } from 'react-router-dom';
-import { FiUser, FiUsers, FiChevronLeft, FiChevronRight, FiMenu } from
"react-icons/fi";
+ Spacer,
+} from "@chakra-ui/react";
+import { Link } from "react-router-dom";
+import {
+ FiUser,
+ FiUsers,
+ FiChevronLeft,
+ FiChevronRight,
+ FiMenu,
+} from "react-icons/fi";
import { AiOutlineAppstore } from "react-icons/ai";
import { IconType } from "react-icons";
import { MdLogout } from "react-icons/md";
-import { useAuth } from 'react-oidc-context';
+import { useAuth } from "react-oidc-context";
interface NavContainerProps {
activeTab: string;
@@ -57,157 +63,211 @@ interface NavItemProps {
onClose: () => void;
}
-const NavItem = memo(({ to, icon, text, activeTab, isCollapsed, onClose }:
NavItemProps) => {
- const isActive = activeTab.toLowerCase() === text.toLowerCase();
- return (
- <Link to={to} onClick={onClose}>
- <Stack
- direction="row"
- align="center"
- color={isActive ? 'black' : 'default.secondary'}
- py={2}
- px={1}
- _hover={{ bg: 'gray.100' }}
- fontSize="sm"
- >
- <Icon as={icon} />
- {!isCollapsed && <Text fontWeight="semibold">{text}</Text>}
- </Stack>
- </Link>
- );
-});
+const NavItem = memo(
+ ({ to, icon, text, activeTab, isCollapsed, onClose }: NavItemProps) => {
+ const isActive = activeTab.toLowerCase() === text.toLowerCase();
+ return (
+ <Link to={to} onClick={onClose}>
+ <Stack
+ direction="row"
+ align="center"
+ color={isActive ? "black" : "default.secondary"}
+ py={2}
+ px={1}
+ _hover={{ bg: "gray.100" }}
+ fontSize="sm"
+ >
+ <Icon as={icon} />
+ {!isCollapsed && <Text fontWeight="semibold">{text}</Text>}
+ </Stack>
+ </Link>
+ );
+ }
+);
+
+export const NavContainer = memo(
+ ({ activeTab, children }: NavContainerProps) => {
+ const auth = useAuth();
-export const NavContainer = memo(({ activeTab, children }: NavContainerProps)
=> {
- const auth = useAuth();
-
- const [isCollapsed, setIsCollapsed] = useState(() => {
- const saved = localStorage.getItem('navCollapsed');
- return saved ? JSON.parse(saved) : false;
- });
-
- const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
- const { isOpen, onOpen, onClose } = useDisclosure();
+ const [isCollapsed, setIsCollapsed] = useState(() => {
+ const saved = localStorage.getItem("navCollapsed");
+ return saved ? JSON.parse(saved) : false;
+ });
- useEffect(() => {
- const handleResize = () => setIsMobile(window.innerWidth < 768);
- window.addEventListener('resize', handleResize);
- return () => window.removeEventListener('resize', handleResize);
- }, []);
+ const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
+ const { isOpen, onOpen, onClose } = useDisclosure();
- useEffect(() => {
- localStorage.setItem('navCollapsed', JSON.stringify(isCollapsed));
- }, [isCollapsed]);
+ useEffect(() => {
+ const handleResize = () => setIsMobile(window.innerWidth < 768);
+ window.addEventListener("resize", handleResize);
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
- const toggleCollapse = () => {
- setIsCollapsed((prev: boolean) => !prev);
- };
+ useEffect(() => {
+ localStorage.setItem("navCollapsed", JSON.stringify(isCollapsed));
+ }, [isCollapsed]);
+
+ const toggleCollapse = () => {
+ setIsCollapsed((prev: boolean) => !prev);
+ };
+
+ if (isMobile) {
+ return (
+ <>
+ <Box position="fixed" top={4} left={4} zIndex={10}>
+ <Button onClick={onOpen} variant="ghost">
+ <Icon as={FiMenu} w={6} h={6} />
+ </Button>
+ </Box>
+ <Drawer isOpen={isOpen} placement="left" onClose={onClose}>
+ <DrawerOverlay />
+ <DrawerContent bg="#F7F7F7">
+ <DrawerCloseButton />
+ <DrawerBody p={4} display="flex" flexDirection="column">
+ <Heading size="md">Custos Auth Portal</Heading>
+ <Stack direction="column" mt={4}>
+ <NavItem
+ to="/applications"
+ icon={AiOutlineAppstore}
+ text="Applications"
+ activeTab={activeTab}
+ isCollapsed={false}
+ onClose={onClose}
+ />
+ <NavItem
+ to="/groups"
+ icon={FiUsers}
+ text="Groups"
+ activeTab={activeTab}
+ isCollapsed={false}
+ onClose={onClose}
+ />
+ <NavItem
+ to="/users"
+ icon={FiUser}
+ text="Users"
+ activeTab={activeTab}
+ isCollapsed={false}
+ onClose={onClose}
+ />
+ </Stack>
+ <Spacer />
+ <Box>
+ <Text fontWeight="bold">{auth.user?.profile?.name}</Text>
+ <Text fontSize="sm" color="gray.500">
+ {auth.user?.profile?.email}
+ </Text>
+ <Button
+ variant="unstyled"
+ w="fit-content"
+ size="sm"
+ _hover={{ color: "gray.500" }}
+ onClick={async () => {
+ await auth.removeUser();
+ onClose();
+ }}
+ >
+ <Flex alignItems="center" gap={2} w="fit-content">
+ <Icon as={MdLogout} />
+ <Text as="span">Logout</Text>
+ </Flex>
+ </Button>
+ </Box>
+ </DrawerBody>
+ </DrawerContent>
+ </Drawer>
+ <Box p={5} pt={16}>
+ {children}
+ </Box>
+ </>
+ );
+ }
- if (isMobile) {
return (
- <>
- <Box position="fixed" top={4} left={4} zIndex={10}>
- <Button onClick={onOpen} variant="ghost">
- <Icon as={FiMenu} w={6} h={6} />
- </Button>
- </Box>
- <Drawer isOpen={isOpen} placement="left" onClose={onClose}>
- <DrawerOverlay />
- <DrawerContent bg="#F7F7F7">
- <DrawerCloseButton />
- <DrawerBody p={4} display="flex" flexDirection="column">
- <Heading size="md">Custos Auth Portal</Heading>
+ <Grid templateColumns="repeat(15, 1fr)" minHeight="100vh">
+ <GridItem
+ colSpan={isCollapsed ? 1 : 3}
+ minWidth={isCollapsed ? "60px" : "240px"}
+ maxWidth={isCollapsed ? "60px" : "240px"}
+ bg="#F7F7F7"
+ position="fixed"
+ h="100vh"
+ >
+ <Flex
+ h="100vh"
+ p={4}
+ direction="column"
+ justifyContent="space-between"
+ >
+ <Box>
+ <Flex justifyContent="space-between" align="center">
+ {!isCollapsed && <Heading size="md">Custos Portal</Heading>}
+ <Button variant="ghost" onClick={toggleCollapse} size="sm">
+ <Icon as={isCollapsed ? FiChevronRight : FiChevronLeft} />
+ </Button>
+ </Flex>
<Stack direction="column" mt={4}>
- <NavItem to="/applications" icon={AiOutlineAppstore}
text="Applications" activeTab={activeTab} isCollapsed={false} onClose={onClose}
/>
- <NavItem to="/groups" icon={FiUsers} text="Groups"
activeTab={activeTab} isCollapsed={false} onClose={onClose} />
- <NavItem to="/users" icon={FiUser} text="Users"
activeTab={activeTab} isCollapsed={false} onClose={onClose} />
+ <NavItem
+ to="/applications"
+ icon={AiOutlineAppstore}
+ text="Applications"
+ activeTab={activeTab}
+ isCollapsed={isCollapsed}
+ onClose={onClose}
+ />
+ <NavItem
+ to="/groups"
+ icon={FiUsers}
+ text="Groups"
+ activeTab={activeTab}
+ isCollapsed={isCollapsed}
+ onClose={onClose}
+ />
+ <NavItem
+ to="/users"
+ icon={FiUser}
+ text="Users"
+ activeTab={activeTab}
+ isCollapsed={isCollapsed}
+ onClose={onClose}
+ />
</Stack>
- <Spacer />
- <Box>
- <Text fontWeight="bold">{auth.user?.profile?.name}</Text>
- <Text fontSize="sm"
color="gray.500">{auth.user?.profile?.email}</Text>
- <Button
- variant="unstyled"
- w="fit-content"
- size="sm"
- _hover={{ color: "gray.500" }}
- onClick={async () => {
- await auth.removeUser();
- onClose();
- }}
- >
- <Flex alignItems="center" gap={2} w="fit-content">
- <Icon as={MdLogout} />
- <Text as="span">Logout</Text>
- </Flex>
- </Button>
- </Box>
- </DrawerBody>
- </DrawerContent>
- </Drawer>
- <Box p={5} pt={16}>
+ </Box>
+ <Box mt="auto">
+ {!isCollapsed && (
+ <>
+ <Text fontWeight="bold">{auth.user?.profile?.name}</Text>
+ <Text fontSize="sm" color="gray.500">
+ {auth.user?.profile?.email}
+ </Text>
+ </>
+ )}
+ <Button
+ variant="unstyled"
+ w="fit-content"
+ size="sm"
+ _hover={{ color: "gray.500" }}
+ onClick={async () => {
+ await auth.removeUser();
+ }}
+ >
+ <Flex alignItems="center" gap={2} w="fit-content">
+ <Icon as={MdLogout} />
+ {!isCollapsed && <Text as="span">Logout</Text>}
+ </Flex>
+ </Button>
+ </Box>
+ </Flex>
+ </GridItem>
+ <GridItem
+ colSpan={isCollapsed ? 14 : 15}
+ p={10}
+ ml={isCollapsed ? "60px" : "240px"}
+ minWidth="0"
+ >
{children}
- </Box>
- </>
+ </GridItem>
+ </Grid>
);
}
-
- return (
- <Grid templateColumns="repeat(15, 1fr)" minHeight="100vh">
- <GridItem
- colSpan={isCollapsed ? 1 : 3}
- minWidth={isCollapsed ? "60px" : "240px"}
- maxWidth={isCollapsed ? "60px" : "240px"}
- bg="#F7F7F7"
- position="fixed"
- h="100vh"
- >
- <Flex h="100vh" p={4} direction="column"
justifyContent="space-between">
- <Box>
- <Flex justifyContent="space-between" align="center">
- {!isCollapsed && <Heading size="md">Custos Auth Portal</Heading>}
- <Button variant="ghost" onClick={toggleCollapse} size="sm">
- <Icon as={isCollapsed ? FiChevronRight : FiChevronLeft} />
- </Button>
- </Flex>
- <Stack direction="column" mt={4}>
- <NavItem to="/applications" icon={AiOutlineAppstore}
text="Applications" activeTab={activeTab} isCollapsed={isCollapsed}
onClose={onClose} />
- <NavItem to="/groups" icon={FiUsers} text="Groups"
activeTab={activeTab} isCollapsed={isCollapsed} onClose={onClose} />
- <NavItem to="/users" icon={FiUser} text="Users"
activeTab={activeTab} isCollapsed={isCollapsed} onClose={onClose} />
- </Stack>
- </Box>
- <Box mt="auto">
- {!isCollapsed && (
- <>
- <Text fontWeight="bold">{auth.user?.profile?.name}</Text>
- <Text fontSize="sm"
color="gray.500">{auth.user?.profile?.email}</Text>
- </>
- )}
- <Button
- variant="unstyled"
- w="fit-content"
- size="sm"
- _hover={{ color: "gray.500" }}
- onClick={async () => {
- await auth.removeUser();
- }}
- >
- <Flex alignItems="center" gap={2} w="fit-content">
- <Icon as={MdLogout} />
- {!isCollapsed && <Text as="span">Logout</Text>}
- </Flex>
- </Button>
- </Box>
- </Flex>
- </GridItem>
- <GridItem
- colSpan={isCollapsed ? 14 : 12}
- p={10}
- ml={isCollapsed ? "60px" : "240px"}
- minWidth="0"
- overflowY="auto"
- >
- {children}
- </GridItem>
- </Grid>
- );
-});
+);
diff --git a/custos-portal/src/components/StackedBorderBox.tsx
b/custos-portal/src/components/StackedBorderBox.tsx
new file mode 100644
index 000000000..3a80de698
--- /dev/null
+++ b/custos-portal/src/components/StackedBorderBox.tsx
@@ -0,0 +1,23 @@
+import { Divider, Stack } from "@chakra-ui/react";
+
+export const StackedBorderBox = ({
+ children,
+}: {
+ children: React.ReactNode;
+}) => {
+ return (
+ <>
+ <Stack
+ border="1px solid"
+ borderColor="border.neutral.tertiary"
+ rounded="xl"
+ p={8}
+ mt={8}
+ divider={<Divider />}
+ spacing={8}
+ >
+ {children}
+ </Stack>
+ </>
+ );
+};
diff --git a/custos-portal/src/components/Users/UserSettings.tsx
b/custos-portal/src/components/Users/UserSettings.tsx
new file mode 100644
index 000000000..ba768d2ac
--- /dev/null
+++ b/custos-portal/src/components/Users/UserSettings.tsx
@@ -0,0 +1,196 @@
+import { NavContainer } from "../NavContainer";
+import {
+ Box,
+ Flex,
+ Text,
+ Input,
+ Icon,
+ TableContainer,
+ Table,
+ Thead,
+ Tr,
+ Th,
+ Td,
+ Tbody,
+ Stack,
+ FormControl,
+ FormLabel,
+ IconButton,
+ Code,
+} from "@chakra-ui/react";
+import { PageTitle } from "../PageTitle";
+import { ActionButton } from "../ActionButton";
+import { Link, useParams } from "react-router-dom";
+import { FaArrowLeft } from "react-icons/fa6";
+import { LeftRightLayout } from "../LeftRightLayout";
+import { FiTrash2 } from "react-icons/fi";
+import { StackedBorderBox } from "../StackedBorderBox";
+
+const DUMMY_ROLES: any = [
+ {
+ application: "Grafana",
+ role: "grafana:viewer",
+ description: "Grafana Viewer",
+ },
+ {
+ application: "Grafana",
+ role: "grafana:editor",
+ description: "Grafana Editor",
+ },
+ {
+ application: "Grafana",
+ role: "grafana:admin",
+ description: "Grafana Admin",
+ },
+];
+
+const DUMMY_ACTIVITY: any = [
+ {
+ action: "User Created",
+ timestamp: "2021-10-01",
+ },
+ {
+ action: "User Disabled",
+ timestamp: "2021-10-01",
+ },
+ {
+ action: "User Enabled",
+ timestamp: "2021-10-01",
+ },
+];
+
+export const UserSettings = () => {
+ const { email } = useParams();
+
+ return (
+ <>
+ <NavContainer activeTab="Users">
+ <Link to="/users">
+ <Flex alignItems="center" gap={2} color="default.secondary">
+ <Icon as={FaArrowLeft} />
+ <Text fontWeight="bold" fontSize="sm">
+ Back to Users
+ </Text>
+ </Flex>
+ </Link>
+
+ <Flex mt={4} justify="space-between">
+ <Box>
+ <PageTitle>John Doe</PageTitle>
+ <Text color="default.secondary" mt={2}>
+ {email}
+ </Text>
+ </Box>
+ <ActionButton icon={FiTrash2} onClick={() => {}}>
+ Disable User
+ </ActionButton>
+ </Flex>
+
+ <StackedBorderBox>
+ <LeftRightLayout
+ left={<Text fontSize="lg">Basic Information</Text>}
+ right={
+ <>
+ <Stack spacing={4}>
+ <FormControl color="default.default">
+ <FormLabel>Name</FormLabel>
+ <Input type="text" />
+ </FormControl>
+ <FormControl>
+ <FormLabel>Email</FormLabel>
+ <Input type="text" />
+ </FormControl>
+ <FormControl>
+ <FormLabel>Joined</FormLabel>
+ <Input type="text" disabled={true} />
+ </FormControl>
+ <FormControl>
+ <FormLabel>Last Signed In</FormLabel>
+ <Input type="text" disabled={true} />
+ </FormControl>
+ </Stack>
+ </>
+ }
+ />
+
+ <Box>
+ <Text fontSize="lg">Groups</Text>
+ <TableContainer mt={4}>
+ <Table variant="simple">
+ <Thead>
+ <Tr>
+ <Th>Name</Th>
+ <Th>Role</Th>
+ <Th>Owner</Th>
+ <Th>Actions</Th>
+ </Tr>
+ </Thead>
+ <Tbody>
+ <Tr>
+ <Td>
+ <Link to="/groups/1">Group 1</Link>
+ </Td>
+ <Td>Admin</Td>
+ <Td>Stella Zhou</Td>
+ <Td>
+ {/* remove icon */}
+ <IconButton
+ aria-label="Delete Role"
+ icon={<FiTrash2 />}
+ size="sm"
+ bg=""
+ />
+ </Td>
+ </Tr>
+ </Tbody>
+ </Table>
+ </TableContainer>
+ </Box>
+
+ <Box>
+ <Text fontSize="lg">Roles</Text>
+ <Text mt={2} color="gray.600">
+ Through their group memberships, this user has the following
roles
+ </Text>
+
+ <TableContainer mt={4}>
+ <Table variant="simple">
+ <Thead>
+ <Tr>
+ <Th>Application</Th>
+ <Th>Role</Th>
+ <Th>Description</Th>
+ </Tr>
+ </Thead>
+ <Tbody>
+ {DUMMY_ROLES.map((role: any) => (
+ <Tr key={role.role}>
+ <Td>{role.application}</Td>
+ <Td>
+ <Code>{role.role}</Code>
+ </Td>
+ <Td>{role.description}</Td>
+ </Tr>
+ ))}
+ </Tbody>
+ </Table>
+ </TableContainer>
+ </Box>
+
+ <Box>
+ <Text fontSize="lg">Activity</Text>
+ {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ DUMMY_ACTIVITY.map((activity: any) => (
+ <Flex key={activity.action} gap={4} mt={4}>
+ <Text color="gray.400">{activity.timestamp}</Text>
+ <Text fontWeight="bold">{activity.action}</Text>
+ </Flex>
+ ))
+ }
+ </Box>
+ </StackedBorderBox>
+ </NavContainer>
+ </>
+ );
+};
diff --git a/custos-portal/src/components/Users/index.tsx
b/custos-portal/src/components/Users/index.tsx
new file mode 100644
index 000000000..6715c5bb9
--- /dev/null
+++ b/custos-portal/src/components/Users/index.tsx
@@ -0,0 +1,117 @@
+import { NavContainer } from "../NavContainer";
+import {
+ Box,
+ Flex,
+ Text,
+ Input,
+ InputGroup,
+ InputRightElement,
+ Icon,
+ TableContainer,
+ Table,
+ Thead,
+ Tr,
+ Th,
+ Td,
+ Tbody,
+} from "@chakra-ui/react";
+import { PageTitle } from "../PageTitle";
+import { ActionButton } from "../ActionButton";
+import { CiSearch } from "react-icons/ci";
+import { User } from "../../interfaces/Users";
+import { Link } from "react-router-dom";
+
+const DUMMY_DATA: User[] = [
+ {
+ name: "Stella Zhou",
+ email: "[email protected]",
+ joined: "2021-10-01",
+ lastSignedIn: "2021-10-01",
+ },
+ {
+ name: "John Doe",
+ email: "[email protected]",
+ joined: "2021-10-01",
+ lastSignedIn: "2021-10-01",
+ },
+ {
+ name: "Jane Doe",
+ email: "[email protected]",
+ joined: "2021-10-01",
+ lastSignedIn: "2021-10-01",
+ },
+];
+
+export const Users = () => {
+ return (
+ <>
+ <NavContainer activeTab="Users">
+ <Flex justifyContent="space-between" alignItems="flex-start">
+ <Box>
+ <PageTitle>Users</PageTitle>
+ <Text color="gray.500" mt={2}>
+ View and manage the list of all end users.
+ </Text>
+ </Box>
+ </Flex>
+
+ <InputGroup mt={4}>
+ <InputRightElement pointerEvents="none">
+ <Icon as={CiSearch} color="black" />
+ </InputRightElement>
+ <Input
+ type="text"
+ placeholder="Search users"
+ _focus={{
+ borderColor: "black",
+ }}
+ _hover={{
+ borderColor: "black",
+ }}
+ />
+ </InputGroup>
+
+ {/* TABLE */}
+ <TableContainer mt={4}>
+ <Table variant="simple">
+ <Thead>
+ <Tr>
+ <Th>Name</Th>
+ <Th>Email</Th>
+ <Th>Joined</Th>
+ <Th>Last Signed In</Th>
+ <Th>Actions</Th>
+ </Tr>
+ </Thead>
+
+ <Tbody>
+ {DUMMY_DATA.map((user) => (
+ <Tr key={user.email}>
+ <Td>
+ <Link to={`/users/${user.email}`}>
+ <Text
+ color="blue.400"
+ _hover={{
+ color: "blue.600",
+ cursor: "pointer",
+ }}
+ >
+ {user.name}
+ </Text>
+ </Link>
+ </Td>
+ <Td>{user.email}</Td>
+ <Td>{user.joined}</Td>
+ <Td>{user.lastSignedIn}</Td>
+ <Td>
+ <ActionButton onClick={() => {}}>Disable</ActionButton>
+ </Td>
+ </Tr>
+ ))}
+ </Tbody>
+ </Table>
+ </TableContainer>
+ </NavContainer>
+ </>
+ );
+};
diff --git a/custos-portal/src/index.tsx b/custos-portal/src/index.tsx
index 16c862ba4..b4e855719 100644
--- a/custos-portal/src/index.tsx
+++ b/custos-portal/src/index.tsx
@@ -17,29 +17,33 @@
* under the License.
*/
-import { createRoot } from 'react-dom/client';
-import App from './App';
-import { extendTheme, ChakraProvider } from '@chakra-ui/react';
-import { AuthProvider, AuthProviderProps } from 'react-oidc-context';
-import { APP_REDIRECT_URI, BACKEND_URL, CLIENT_ID, TENANT_ID } from
'./lib/constants';
-import { WebStorageStateStore } from 'oidc-client-ts';
-import { useEffect, useState } from 'react';
-import localOidcConfig from './lib/localOidcConfig.json';
+import { createRoot } from "react-dom/client";
+import App from "./App";
+import { extendTheme, ChakraProvider } from "@chakra-ui/react";
+import { AuthProvider, AuthProviderProps } from "react-oidc-context";
+import {
+ APP_REDIRECT_URI,
+ BACKEND_URL,
+ CLIENT_ID,
+ TENANT_ID,
+} from "./lib/constants";
+import { WebStorageStateStore } from "oidc-client-ts";
+import { useEffect, useState } from "react";
const theme = extendTheme({
colors: {
default: {
- "default": "#1E1E1E",
- "secondary": "#757575",
- "tertiary": "#B3B3B3"
+ default: "#1E1E1E",
+ secondary: "#757575",
+ tertiary: "#B3B3B3",
},
border: {
neutral: {
- "default": "#303030",
- "secondary": "#767676",
- "tertiary": "#B2B2B2",
- }
- }
+ default: "#303030",
+ secondary: "#767676",
+ tertiary: "#B2B2B2",
+ },
+ },
},
});
@@ -50,7 +54,9 @@ const Index = () => {
const fetchOidcConfig = async () => {
try {
let data;
- const response = await
fetch(`${BACKEND_URL}/api/v1/identity-management/tenant/${TENANT_ID}/.well-known/openid-configuration`);
// Replace with actual API endpoint
+ const response = await fetch(
+
`${BACKEND_URL}/api/v1/identity-management/tenant/${TENANT_ID}/.well-known/openid-configuration`
+ ); // Replace with actual API endpoint
data = await response.json();
const redirectUri = APP_REDIRECT_URI;
@@ -58,8 +64,8 @@ const Index = () => {
authority: `${BACKEND_URL}/api/v1/identity-management/`,
client_id: CLIENT_ID,
redirect_uri: redirectUri,
- response_type: 'code',
- scope: 'openid email',
+ response_type: "code",
+ scope: "openid email",
metadata: {
authorization_endpoint: data.authorization_endpoint,
token_endpoint: data.token_endpoint,
@@ -74,7 +80,7 @@ const Index = () => {
setOidcConfig(theConfig);
} catch (error) {
- console.error('Error fetching OIDC config:', error);
+ console.error("Error fetching OIDC config:", error);
}
};
@@ -90,8 +96,8 @@ const Index = () => {
<AuthProvider
{...oidcConfig}
onSigninCallback={async (user) => {
- console.log('User signed in', user);
- window.location.href = '/groups';
+ console.log("User signed in", user);
+ window.location.href = "/groups";
}}
>
<App />
@@ -100,6 +106,6 @@ const Index = () => {
);
};
-const container = document.getElementById('root') as HTMLElement;
+const container = document.getElementById("root") as HTMLElement;
const root = createRoot(container);
root.render(<Index />);
diff --git a/custos-portal/src/interfaces/Users.tsx
b/custos-portal/src/interfaces/Users.tsx
new file mode 100644
index 000000000..83bf47471
--- /dev/null
+++ b/custos-portal/src/interfaces/Users.tsx
@@ -0,0 +1,6 @@
+export interface User {
+ name: string;
+ email: string;
+ joined: string;
+ lastSignedIn: string;
+}
diff --git a/custos-portal/src/lib/constants.ts
b/custos-portal/src/lib/constants.ts
index 11ccbe5e1..652cc4cd5 100644
--- a/custos-portal/src/lib/constants.ts
+++ b/custos-portal/src/lib/constants.ts
@@ -20,13 +20,9 @@
import packageJson from '../../package.json';
export const PORTAL_VERSION = packageJson.version;
-export let CLIENT_ID:string;
-export let BACKEND_URL:string;
-export let APP_URL:string;
+export const CLIENT_ID = 'custos-gcq8jxkwpvs2gcudzmfn-10000000';;
+export const BACKEND_URL = 'http://localhost:8081';
+export const APP_URL = "http://localhost:5173";
- CLIENT_ID = 'custos-kgap8hu6ih4hddvlzzlb-10000000';
- BACKEND_URL = 'https://api.playground.usecustos.org';
- APP_URL = 'http://localhost:5173'
-
-export const APP_REDIRECT_URI = `${APP_URL}/oauth-callback`;
+export const APP_REDIRECT_URI = `${APP_URL}/callback/`;
export const TENANT_ID = '10000000';
\ No newline at end of file