repo details page
Signed-off-by: Raul Kele <raulkeleblk@gmail.com>
This commit is contained in:
parent
8f91259e4e
commit
a84ad70fa2
359
package-lock.json
generated
359
package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
"@emotion/styled": "^11.9.3",
|
||||
"@mui-treasury/styles": "^1.13.1",
|
||||
"@mui/icons-material": "^5.2.5",
|
||||
"@mui/lab": "^5.0.0-alpha.89",
|
||||
"@mui/material": "^5.8.6",
|
||||
"@mui/styles": "^5.8.6",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
@ -1933,6 +1934,75 @@
|
||||
"postcss": "^8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@date-io/core": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/core/-/core-2.14.0.tgz",
|
||||
"integrity": "sha512-qFN64hiFjmlDHJhu+9xMkdfDG2jLsggNxKXglnekUpXSq8faiqZgtHm2lsHCUuaPDTV6wuXHcCl8J1GQ5wLmPw=="
|
||||
},
|
||||
"node_modules/@date-io/date-fns": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-2.14.0.tgz",
|
||||
"integrity": "sha512-4fJctdVyOd5cKIKGaWUM+s3MUXMuzkZaHuTY15PH70kU1YTMrCoauA7hgQVx9qj0ZEbGrH9VSPYJYnYro7nKiA==",
|
||||
"dependencies": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"date-fns": "^2.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"date-fns": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@date-io/dayjs": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/dayjs/-/dayjs-2.14.0.tgz",
|
||||
"integrity": "sha512-4fRvNWaOh7AjvOyJ4h6FYMS7VHLQnIEeAV5ahv6sKYWx+1g1UwYup8h7+gPuoF+sW2hTScxi7PVaba2Jk/U8Og==",
|
||||
"dependencies": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"dayjs": "^1.8.17"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"dayjs": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@date-io/luxon": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/luxon/-/luxon-2.14.0.tgz",
|
||||
"integrity": "sha512-KmpBKkQFJ/YwZgVd0T3h+br/O0uL9ZdE7mn903VPAG2ZZncEmaUfUdYKFT7v7GyIKJ4KzCp379CRthEbxevEVg==",
|
||||
"dependencies": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"luxon": "^1.21.3 || ^2.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"luxon": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@date-io/moment": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/moment/-/moment-2.14.0.tgz",
|
||||
"integrity": "sha512-VsoLXs94GsZ49ecWuvFbsa081zEv2xxG7d+izJsqGa2L8RPZLlwk27ANh87+SNnOUpp+qy2AoCAf0mx4XXhioA==",
|
||||
"dependencies": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"moment": "^2.24.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"moment": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
|
||||
@ -3061,6 +3131,97 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/lab": {
|
||||
"version": "5.0.0-alpha.89",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.89.tgz",
|
||||
"integrity": "sha512-u5bMi/V+Utwouo9awVzGasj/LudlRqPFyMo2L5/y60uFo0EaG17bt1jh/U7smQCdjd+7tvJ39HNMkEmIoGr7BQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@mui/base": "5.0.0-alpha.88",
|
||||
"@mui/system": "^5.8.7",
|
||||
"@mui/utils": "^5.8.6",
|
||||
"@mui/x-date-pickers": "5.0.0-alpha.1",
|
||||
"clsx": "^1.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^17.0.2",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"rifm": "^0.12.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material": "^5.0.0",
|
||||
"@types/react": "^17.0.0 || ^18.0.0",
|
||||
"date-fns": "^2.25.0",
|
||||
"dayjs": "^1.10.7",
|
||||
"luxon": "^1.28.0 || ^2.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns": {
|
||||
"optional": true
|
||||
},
|
||||
"dayjs": {
|
||||
"optional": true
|
||||
},
|
||||
"luxon": {
|
||||
"optional": true
|
||||
},
|
||||
"moment": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/lab/node_modules/@mui/base": {
|
||||
"version": "5.0.0-alpha.88",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.88.tgz",
|
||||
"integrity": "sha512-uL7ej2F/3GUnZewsDQSHUVHoSBT3AQcTIdfdy6QeCHy7X26mtbcIvTRcjl2PzbbNQplppavSTibPiQG/giJ+ng==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@emotion/is-prop-valid": "^1.1.3",
|
||||
"@mui/types": "^7.1.4",
|
||||
"@mui/utils": "^5.8.6",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"clsx": "^1.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^17.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "5.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.8.6.tgz",
|
||||
@ -3131,12 +3292,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/styled-engine": {
|
||||
"version": "5.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.8.0.tgz",
|
||||
"integrity": "sha512-Q3spibB8/EgeMYHc+/o3RRTnAYkSl7ROCLhXJ830W8HZ2/iDiyYp16UcxKPurkXvLhUaILyofPVrP3Su2uKsAw==",
|
||||
"version": "5.8.7",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.8.7.tgz",
|
||||
"integrity": "sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@emotion/cache": "^11.7.1",
|
||||
"@emotion/cache": "^11.9.3",
|
||||
"csstype": "^3.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -3201,16 +3363,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "5.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.8.6.tgz",
|
||||
"integrity": "sha512-+a+rD58XltKQHDrrjcuCta2cUBqdnLDUDwnphSLCMFigRl8/uk+R+fdQRlMNRXAOgnMb8ioWIgfjxri5pmTH4A==",
|
||||
"version": "5.8.7",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.8.7.tgz",
|
||||
"integrity": "sha512-yFoFbfO42FWeSUDrFPixYjpqySQMqVMOSbSlAxiKnwFpvXGGn/bkfQTboCRNO31fvES29FJLQd4mwwMHd5mXng==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@mui/private-theming": "^5.8.6",
|
||||
"@mui/styled-engine": "^5.8.0",
|
||||
"@mui/styled-engine": "^5.8.7",
|
||||
"@mui/types": "^7.1.4",
|
||||
"@mui/utils": "^5.8.6",
|
||||
"clsx": "^1.1.1",
|
||||
"clsx": "^1.2.0",
|
||||
"csstype": "^3.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
@ -3274,6 +3436,53 @@
|
||||
"react": "^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-date-pickers": {
|
||||
"version": "5.0.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.0-alpha.1.tgz",
|
||||
"integrity": "sha512-dLPkRiIn2Gr0momblxiOnIwrxn4SijVix+8e08mwAGWhiWcmWep1O9XTRDpZsjB0kjHYCf+kZjlRX4dxnj2acg==",
|
||||
"dependencies": {
|
||||
"@date-io/date-fns": "^2.11.0",
|
||||
"@date-io/dayjs": "^2.11.0",
|
||||
"@date-io/luxon": "^2.11.1",
|
||||
"@date-io/moment": "^2.11.0",
|
||||
"@mui/utils": "^5.6.0",
|
||||
"clsx": "^1.1.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"rifm": "^0.12.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mui/material": "^5.2.3",
|
||||
"@mui/system": "^5.2.3",
|
||||
"date-fns": "^2.25.0",
|
||||
"dayjs": "^1.10.7",
|
||||
"luxon": "^1.28.0 || ^2.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"react": "^17.0.2 || ^18.0.0",
|
||||
"react-dom": "^17.0.2 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"date-fns": {
|
||||
"optional": true
|
||||
},
|
||||
"dayjs": {
|
||||
"optional": true
|
||||
},
|
||||
"luxon": {
|
||||
"optional": true
|
||||
},
|
||||
"moment": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@ -5822,9 +6031,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
|
||||
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -16721,6 +16930,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rifm": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/rifm/-/rifm-0.12.1.tgz",
|
||||
"integrity": "sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
@ -20487,6 +20704,43 @@
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"@date-io/core": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/core/-/core-2.14.0.tgz",
|
||||
"integrity": "sha512-qFN64hiFjmlDHJhu+9xMkdfDG2jLsggNxKXglnekUpXSq8faiqZgtHm2lsHCUuaPDTV6wuXHcCl8J1GQ5wLmPw=="
|
||||
},
|
||||
"@date-io/date-fns": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-2.14.0.tgz",
|
||||
"integrity": "sha512-4fJctdVyOd5cKIKGaWUM+s3MUXMuzkZaHuTY15PH70kU1YTMrCoauA7hgQVx9qj0ZEbGrH9VSPYJYnYro7nKiA==",
|
||||
"requires": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
}
|
||||
},
|
||||
"@date-io/dayjs": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/dayjs/-/dayjs-2.14.0.tgz",
|
||||
"integrity": "sha512-4fRvNWaOh7AjvOyJ4h6FYMS7VHLQnIEeAV5ahv6sKYWx+1g1UwYup8h7+gPuoF+sW2hTScxi7PVaba2Jk/U8Og==",
|
||||
"requires": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
}
|
||||
},
|
||||
"@date-io/luxon": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/luxon/-/luxon-2.14.0.tgz",
|
||||
"integrity": "sha512-KmpBKkQFJ/YwZgVd0T3h+br/O0uL9ZdE7mn903VPAG2ZZncEmaUfUdYKFT7v7GyIKJ4KzCp379CRthEbxevEVg==",
|
||||
"requires": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
}
|
||||
},
|
||||
"@date-io/moment": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-io/moment/-/moment-2.14.0.tgz",
|
||||
"integrity": "sha512-VsoLXs94GsZ49ecWuvFbsa081zEv2xxG7d+izJsqGa2L8RPZLlwk27ANh87+SNnOUpp+qy2AoCAf0mx4XXhioA==",
|
||||
"requires": {
|
||||
"@date-io/core": "^2.14.0"
|
||||
}
|
||||
},
|
||||
"@emotion/babel-plugin": {
|
||||
"version": "11.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
|
||||
@ -21287,6 +21541,40 @@
|
||||
"@babel/runtime": "^7.17.2"
|
||||
}
|
||||
},
|
||||
"@mui/lab": {
|
||||
"version": "5.0.0-alpha.89",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.89.tgz",
|
||||
"integrity": "sha512-u5bMi/V+Utwouo9awVzGasj/LudlRqPFyMo2L5/y60uFo0EaG17bt1jh/U7smQCdjd+7tvJ39HNMkEmIoGr7BQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@mui/base": "5.0.0-alpha.88",
|
||||
"@mui/system": "^5.8.7",
|
||||
"@mui/utils": "^5.8.6",
|
||||
"@mui/x-date-pickers": "5.0.0-alpha.1",
|
||||
"clsx": "^1.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^17.0.2",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"rifm": "^0.12.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mui/base": {
|
||||
"version": "5.0.0-alpha.88",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.88.tgz",
|
||||
"integrity": "sha512-uL7ej2F/3GUnZewsDQSHUVHoSBT3AQcTIdfdy6QeCHy7X26mtbcIvTRcjl2PzbbNQplppavSTibPiQG/giJ+ng==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@emotion/is-prop-valid": "^1.1.3",
|
||||
"@mui/types": "^7.1.4",
|
||||
"@mui/utils": "^5.8.6",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"clsx": "^1.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^17.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/material": {
|
||||
"version": "5.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.8.6.tgz",
|
||||
@ -21316,12 +21604,13 @@
|
||||
}
|
||||
},
|
||||
"@mui/styled-engine": {
|
||||
"version": "5.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.8.0.tgz",
|
||||
"integrity": "sha512-Q3spibB8/EgeMYHc+/o3RRTnAYkSl7ROCLhXJ830W8HZ2/iDiyYp16UcxKPurkXvLhUaILyofPVrP3Su2uKsAw==",
|
||||
"version": "5.8.7",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.8.7.tgz",
|
||||
"integrity": "sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@emotion/cache": "^11.7.1",
|
||||
"@emotion/cache": "^11.9.3",
|
||||
"csstype": "^3.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
}
|
||||
},
|
||||
@ -21350,16 +21639,16 @@
|
||||
}
|
||||
},
|
||||
"@mui/system": {
|
||||
"version": "5.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.8.6.tgz",
|
||||
"integrity": "sha512-+a+rD58XltKQHDrrjcuCta2cUBqdnLDUDwnphSLCMFigRl8/uk+R+fdQRlMNRXAOgnMb8ioWIgfjxri5pmTH4A==",
|
||||
"version": "5.8.7",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.8.7.tgz",
|
||||
"integrity": "sha512-yFoFbfO42FWeSUDrFPixYjpqySQMqVMOSbSlAxiKnwFpvXGGn/bkfQTboCRNO31fvES29FJLQd4mwwMHd5mXng==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@mui/private-theming": "^5.8.6",
|
||||
"@mui/styled-engine": "^5.8.0",
|
||||
"@mui/styled-engine": "^5.8.7",
|
||||
"@mui/types": "^7.1.4",
|
||||
"@mui/utils": "^5.8.6",
|
||||
"clsx": "^1.1.1",
|
||||
"clsx": "^1.2.0",
|
||||
"csstype": "^3.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
}
|
||||
@ -21382,6 +21671,22 @@
|
||||
"react-is": "^17.0.2"
|
||||
}
|
||||
},
|
||||
"@mui/x-date-pickers": {
|
||||
"version": "5.0.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.0-alpha.1.tgz",
|
||||
"integrity": "sha512-dLPkRiIn2Gr0momblxiOnIwrxn4SijVix+8e08mwAGWhiWcmWep1O9XTRDpZsjB0kjHYCf+kZjlRX4dxnj2acg==",
|
||||
"requires": {
|
||||
"@date-io/date-fns": "^2.11.0",
|
||||
"@date-io/dayjs": "^2.11.0",
|
||||
"@date-io/luxon": "^2.11.1",
|
||||
"@date-io/moment": "^2.11.0",
|
||||
"@mui/utils": "^5.6.0",
|
||||
"clsx": "^1.1.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"rifm": "^0.12.1"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@ -23283,9 +23588,9 @@
|
||||
}
|
||||
},
|
||||
"clsx": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
|
||||
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
@ -30957,6 +31262,12 @@
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
|
||||
},
|
||||
"rifm": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/rifm/-/rifm-0.12.1.tgz",
|
||||
"integrity": "sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==",
|
||||
"requires": {}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
|
@ -7,6 +7,7 @@
|
||||
"@emotion/styled": "^11.9.3",
|
||||
"@mui-treasury/styles": "^1.13.1",
|
||||
"@mui/icons-material": "^5.2.5",
|
||||
"@mui/lab": "^5.0.0-alpha.89",
|
||||
"@mui/material": "^5.8.6",
|
||||
"@mui/styles": "^5.8.6",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react'
|
||||
import HomePage from './pages/HomePage.jsx'
|
||||
import LoginPage from './pages/LoginPage.jsx'
|
||||
import ImageDetails from './components/ImageDetails.jsx'
|
||||
import RepoDetails from './components/RepoDetails.jsx'
|
||||
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
|
||||
@ -9,6 +9,7 @@ import './App.css';
|
||||
|
||||
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
|
||||
import { AuthWrapper } from 'utilities/AuthWrapper.jsx';
|
||||
import RepoPage from 'pages/RepoPage.jsx';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
|
||||
@ -36,7 +37,7 @@ function App() {
|
||||
<Route element={<AuthWrapper isLoggedIn={isLoggedIn} redirect="/login" />}>
|
||||
<Route path="/" element={<Navigate to="/home" />} />
|
||||
<Route path="/home" element={<HomePage keywords={searchKeywords} updateKeywords={setSearchKeywords} data={data} updateData={setData} />} />
|
||||
<Route path="/image/:name" element={<ImageDetails username={username} password={password} />} />
|
||||
<Route path="/image/:name" element={<RepoPage />} />
|
||||
</Route>
|
||||
<Route element={<AuthWrapper isLoggedIn={!isLoggedIn} redirect="/"/>}>
|
||||
<Route path="/login" element={<LoginPage username={username} password={password} updateUsername={setUsername} updatePassword={setPassword} isAuthEnabled={isAuthEnabled} setIsAuthEnabled={setIsAuthEnabled} isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} />} />
|
||||
|
@ -62,4 +62,9 @@ const api = {
|
||||
|
||||
};
|
||||
|
||||
export default api;
|
||||
const endpoints = {
|
||||
imageList: '/v2/_zot/ext/search?query={ImageListWithLatestTag () { Name Latest LastUpdated Description Licenses Vendor Size Labels}}',
|
||||
detailedRepoInfo: (name) => `/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Manifests {Digest Tag Layers {Size Digest}}}}`
|
||||
}
|
||||
|
||||
export {api, endpoints};
|
||||
|
@ -11,8 +11,7 @@ import { Container, FormControl, Grid, InputLabel, Select, Stack } from '@mui/ma
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
|
||||
// utility
|
||||
import api from '../api.js';
|
||||
import {URL} from '../constants';
|
||||
import {api, endpoints} from '../api';
|
||||
import {host} from '../constants';
|
||||
import {isEmpty} from 'lodash';
|
||||
import FilterCard from './FilterCard.jsx';
|
||||
@ -44,7 +43,7 @@ function Explore ({ keywords, data, updateData }) {
|
||||
const classes = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
api.get(`${host}${URL.imageList}`)
|
||||
api.get(`${host}${endpoints.imageList}`)
|
||||
.then(response => {
|
||||
if (response.data && response.data.data) {
|
||||
let imageList = response.data.data.ImageListWithLatestTag;
|
||||
|
@ -14,14 +14,16 @@ const useStyles = makeStyles((theme) => {
|
||||
console.log("theme", theme)
|
||||
return {
|
||||
exploreHeader: {
|
||||
backgroundColor: "#fafafa",
|
||||
backgroundColor: "#FFFFFF",
|
||||
minHeight: 50,
|
||||
paddingLeft: 5,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
},
|
||||
explore: {
|
||||
color: 'gray'
|
||||
color: '#00000099',
|
||||
letterSpacing: "0.15px"
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -33,9 +35,9 @@ function ExploreHeader() {
|
||||
|
||||
return (
|
||||
<div className={classes.exploreHeader}>
|
||||
<Breadcrumbs separator={<NavigateNextIcon fontSize="small" />} aria-label="breadcrumb">
|
||||
<Link to="/"><Typography className={classes.explore}>Explore Packages</Typography></Link>
|
||||
{ path.includes('/image/') && <Typography>{path.replace('/image/', '')}</Typography> }
|
||||
<Breadcrumbs separator="/" aria-label="breadcrumb">
|
||||
<Link to="/"><Typography variant="body1" className={classes.explore}>Home</Typography></Link>
|
||||
{ path.includes('/image/') && <Typography className={classes.explore} variant="body1">{path.replace('/image/', '')}</Typography> }
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
);
|
||||
|
@ -168,7 +168,6 @@ function Header({ updateKeywords }) {
|
||||
</Popper>
|
||||
</div>
|
||||
</Toolbar>
|
||||
{ path !== '/login' && path !== '/' && path !== '/home' && <ExploreHeader /> }
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
|
@ -1,111 +0,0 @@
|
||||
// react global
|
||||
import { useParams, useLocation } from 'react-router-dom'
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
// utility
|
||||
import axios from 'axios';
|
||||
|
||||
// components
|
||||
import Header from './Header.jsx'
|
||||
import RepoCard from './RepoCard.jsx'
|
||||
import Tags from './Tags.jsx'
|
||||
import {Container, Box, Grid} from '@mui/material';
|
||||
import { URL } from '../constants';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
pageWrapper: {
|
||||
backgroundColor: "#f2f2f2a1",
|
||||
},
|
||||
container: {
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5,
|
||||
marginTop: 100,
|
||||
backgroundColor: "#f2f2f2a1",
|
||||
},
|
||||
parentWrapper: {
|
||||
height: '100vh',
|
||||
},
|
||||
gridWrapper: {
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
backgroundColor: "#fff",
|
||||
border: "1px #f2f2f2 dashed",
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
function ImageDetails (props) {
|
||||
const {host, username, password} = props;
|
||||
const [imageDetailData, setImageDetailData] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// get data from <Link here
|
||||
const location = useLocation();
|
||||
const myData = location && location.state && location.state.data;
|
||||
|
||||
// get url param from <Route here (i.e. image name)
|
||||
const {name} = useParams();
|
||||
const classes = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
const {name, version} = myData;
|
||||
|
||||
const token = btoa(username + ':' + password);
|
||||
const cfg = {
|
||||
headers: {
|
||||
'Authorization': `Basic ${token}`,
|
||||
}
|
||||
};
|
||||
|
||||
axios.get(`${host}${URL.imageList}`, cfg)
|
||||
.then(response => {
|
||||
if (response.data && response.data.data) {
|
||||
let imageList = response.data.data.ExpandedRepoInfo;
|
||||
let imageData = {
|
||||
name: name,
|
||||
tags: imageList.Manifests
|
||||
}
|
||||
setImageDetailData(imageData);
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setImageDetailData({});
|
||||
});
|
||||
}, [])
|
||||
|
||||
|
||||
return (
|
||||
<div className={classes.pageWrapper}>
|
||||
<Header updateKeywords={props.updateKeywords}></Header>
|
||||
<Container maxWidth="md" className={classes.container}>
|
||||
<div className={classes.parentWrapper}>
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
<Grid item md={1} ></Grid>
|
||||
<Grid item md={10}>
|
||||
<Box>
|
||||
<RepoCard className={classes.tile}
|
||||
name={myData.name}
|
||||
version={myData.latestVersion}
|
||||
description={myData.description}
|
||||
tags={myData.tags}
|
||||
vendor={myData.vendor}
|
||||
size={myData.size}
|
||||
licenses={myData.licenses}
|
||||
key={myData}
|
||||
size="lg"
|
||||
shown={true}
|
||||
/>
|
||||
</Box>
|
||||
<Tags data={imageDetailData} />
|
||||
</Grid>
|
||||
<Grid item md={1} ></Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ImageDetails;
|
@ -70,8 +70,8 @@ function RepoCard(props) {
|
||||
const navigate = useNavigate();
|
||||
const {name, vendor, description, lastUpdated, downloads, rating} = props;
|
||||
|
||||
const goToDetails = () => {
|
||||
navigate(`/image/${name}`);
|
||||
const goToDetails = (repo) => {
|
||||
navigate(`/image/${name}`, {state: {lastDate: (lastUpdated? DateTime.fromISO(lastUpdated) : DateTime.now().minus({days:1})).toRelative({unit:'days'})}});
|
||||
}
|
||||
|
||||
const verifiedCheck = () => {
|
||||
|
296
src/components/RepoDetails.jsx
Normal file
296
src/components/RepoDetails.jsx
Normal file
@ -0,0 +1,296 @@
|
||||
// react global
|
||||
import { useLocation, useParams } from 'react-router-dom'
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
// utility
|
||||
import {api, endpoints} from '../api';
|
||||
import mockData from '../utilities/mockData';
|
||||
|
||||
// components
|
||||
import Tags from './Tags.jsx'
|
||||
import {Box, Card, CardContent, CardMedia, Chip, FormControl, Grid, IconButton, InputAdornment, OutlinedInput, Stack, Tab, Typography} from '@mui/material';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { host } from '../constants';
|
||||
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
|
||||
// placeholder images
|
||||
import repocube1 from '../assets/repocube-1.png';
|
||||
import repocube2 from '../assets/repocube-2.png';
|
||||
import repocube3 from '../assets/repocube-3.png';
|
||||
import repocube4 from '../assets/repocube-4.png';
|
||||
import { TabContext, TabList, TabPanel } from '@mui/lab';
|
||||
import RepoDetailsMetadata from './RepoDetailsMetadata';
|
||||
|
||||
// @ts-ignore
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
pageWrapper: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
},
|
||||
container: {
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5,
|
||||
marginTop: 100,
|
||||
backgroundColor: "#FFFFFF",
|
||||
},
|
||||
parentWrapper: {
|
||||
height: '100vh',
|
||||
},
|
||||
gridWrapper: {
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
backgroundColor: "#FFFFFF",
|
||||
border: "1px #f2f2f2 dashed",
|
||||
},
|
||||
avatar: {
|
||||
height:"48px",
|
||||
width:"48px"
|
||||
},
|
||||
cardBtn: {
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
},
|
||||
media: {
|
||||
borderRadius: '50px',
|
||||
},
|
||||
tabs: {
|
||||
marginTop: "5%",
|
||||
border: 1,
|
||||
borderColor: 'divider',
|
||||
padding:"8px",
|
||||
boxShadow: "0px 5px 10px rgba(131, 131, 131, 0.08)",
|
||||
background:"#EDE7F6",
|
||||
borderRadius:"32px",
|
||||
height: "100%"
|
||||
},
|
||||
selectedTab: {
|
||||
background:"#A53692",
|
||||
borderRadius:"24px"
|
||||
},
|
||||
tabPanel: {
|
||||
height:"100%"
|
||||
},
|
||||
metadata: {
|
||||
padding:"24px"
|
||||
},
|
||||
card: {
|
||||
marginBottom: 2,
|
||||
display:"flex",
|
||||
flexDirection:"row",
|
||||
alignItems:"start",
|
||||
background:"#FFFFFF",
|
||||
boxShadow:"0px 5px 10px rgba(131, 131, 131, 0.08)",
|
||||
borderRadius:"24px",
|
||||
flex:"none",
|
||||
alignSelf:"stretch",
|
||||
flexGrow:0,
|
||||
order:0,
|
||||
width:"100%"
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
// temporary utility to get image
|
||||
const randomIntFromInterval = (min, max) => {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
};
|
||||
|
||||
const randomImage = () => {
|
||||
const imageArray = [repocube1,repocube2,repocube3,repocube4];
|
||||
return imageArray[randomIntFromInterval(0,3)];
|
||||
};
|
||||
|
||||
function RepoDetails (props) {
|
||||
const [repoDetailData, setRepoDetailData] = useState({});
|
||||
// @ts-ignore
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedTab, setSelectedTab] = useState("Readme");
|
||||
|
||||
// get url param from <Route here (i.e. image name)
|
||||
const {name} = useParams();
|
||||
const {state} = useLocation();
|
||||
// @ts-ignore
|
||||
const {lastDate} = state;
|
||||
const classes = useStyles();
|
||||
const {description, readmeTitle, dependencies, dependents} = props;
|
||||
|
||||
useEffect(() => {
|
||||
api.get(`${host}${endpoints.detailedRepoInfo(name)}`)
|
||||
.then(response => {
|
||||
if (response.data && response.data.data) {
|
||||
let imageList = response.data.data.ExpandedRepoInfo;
|
||||
let imageData = {
|
||||
name: name,
|
||||
tags: imageList.Manifests
|
||||
}
|
||||
setRepoDetailData(imageData);
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
setRepoDetailData({});
|
||||
});
|
||||
}, [])
|
||||
|
||||
const getLatestManifest = () => {
|
||||
// @ts-ignore
|
||||
const manifests = repoDetailData.tags || [{}];
|
||||
return manifests[0];
|
||||
}
|
||||
|
||||
const getLatestLayer = () => {
|
||||
const layers = getLatestManifest().Layers || [{}];
|
||||
return layers[0];
|
||||
}
|
||||
|
||||
const verifiedCheck = () => {
|
||||
return (<CheckCircleOutlineOutlinedIcon sx={{color:"#388E3C!important"}}/>);
|
||||
}
|
||||
|
||||
const platformChips = () => {
|
||||
// if platforms not received, mock data
|
||||
const platforms = props.platforms || ["Windows","PowerPC64LE","IBM Z","Linux"];
|
||||
return platforms.map((platform, index) => (
|
||||
<Chip key={index} label={platform} sx={{backgroundColor:"#EDE7F6", color: "#311B92"}}/>
|
||||
));
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const handleTabChange = (event, newValue) => {
|
||||
setSelectedTab(newValue);
|
||||
};
|
||||
|
||||
const renderReadme = () => {
|
||||
return (
|
||||
<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h4" align="left">{readmeTitle || 'Quickstart'}</Typography>
|
||||
<Typography variant="body1">{description || mockData.loremIpsum}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const renderDependencies = () => {
|
||||
return (<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h4" align="left">Dependecies ({dependencies || '---'})</Typography>
|
||||
</CardContent>
|
||||
</Card>);
|
||||
};
|
||||
|
||||
const renderDependents = () => {
|
||||
return (<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h4" align="left">Dependents ({dependents || '---'})</Typography>
|
||||
</CardContent>
|
||||
</Card>);
|
||||
};
|
||||
|
||||
const renderVulnerabilities = () => {
|
||||
return (<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h4" align="left">Vulnerabilities</Typography>
|
||||
</CardContent>
|
||||
</Card>);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className={classes.pageWrapper}>
|
||||
<div className={classes.parentWrapper}>
|
||||
<Card variant="outlined">
|
||||
<CardContent>
|
||||
<Grid container>
|
||||
<Grid item xs={7}>
|
||||
<Stack alignItems="center" direction="row" spacing={2}>
|
||||
<CardMedia classes={{
|
||||
root: classes.media,
|
||||
img: classes.avatar,
|
||||
}}
|
||||
component="img"
|
||||
image={randomImage()}
|
||||
alt="icon"
|
||||
/>
|
||||
<Typography variant="h5" component="div">
|
||||
{name}
|
||||
</Typography>
|
||||
<Chip label="Verified license" sx={{backgroundColor:"#E8F5E9", color:"#388E3C"}} variant="filled" onDelete={() => {return}} deleteIcon={verifiedCheck()}/>
|
||||
</Stack>
|
||||
<Typography pt={1} sx={{ fontSize: 12 }} gutterBottom align="left">
|
||||
{description || 'The complete solution for node.js command-line programs'}
|
||||
</Typography>
|
||||
<Stack alignItems="center" direction="row" spacing={2} pt={1}>
|
||||
{platformChips()}
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={5} pt={2}>
|
||||
<Typography variant="body1">Copy and pull to pull this image</Typography>
|
||||
<FormControl sx={{ m: 1, width: '25ch' }} variant="outlined">
|
||||
<OutlinedInput
|
||||
value={`Pull ${name}`}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton aria-label='copy' edge="end" onClick={() => navigator.clipboard.writeText(`Pull ${name}`)}>
|
||||
<ContentCopyIcon/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
aria-describedby="outlined-weight-helper-text"
|
||||
inputProps={{
|
||||
'aria-label': 'weight',
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<TabContext value={selectedTab}>
|
||||
<Box className={classes.tabs}>
|
||||
<TabList onChange={handleTabChange} TabIndicatorProps={{ className: classes.selectedTab }} >
|
||||
<Tab value="Readme" label="Read me"/>
|
||||
<Tab value="Tags" label="Tags"/>
|
||||
<Tab value="Dependencies" label={`${dependencies || 0} dependencies`}/>
|
||||
<Tab value="Dependents" label={`${dependents || 0} dependents`}/>
|
||||
<Tab value="Vulnerabilities" label="Vulnerabilities"/>
|
||||
<Tab value="6" label="Tab 6"/>
|
||||
<Tab value="7" label="Tab 7"/>
|
||||
<Tab value="8" label="Tab 8"/>
|
||||
</TabList>
|
||||
<Grid container>
|
||||
<Grid item xs={8}>
|
||||
<TabPanel value="Readme" className={classes.tabPanel}>
|
||||
{renderReadme()}
|
||||
</TabPanel>
|
||||
<TabPanel value="Tags" className={classes.tabPanel}>
|
||||
<Tags data={repoDetailData} />
|
||||
</TabPanel>
|
||||
<TabPanel value="Dependencies" className={classes.tabPanel}>
|
||||
{renderDependencies()}
|
||||
</TabPanel>
|
||||
<TabPanel value="Dependents" className={classes.tabPanel}>
|
||||
{renderDependents()}
|
||||
</TabPanel>
|
||||
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
||||
{renderVulnerabilities()}
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
<Grid item xs={4} className={classes.metadata}>
|
||||
<RepoDetailsMetadata
|
||||
name={name}
|
||||
lastUpdated={lastDate}
|
||||
size={getLatestLayer()?.size}
|
||||
latestTag={getLatestManifest()?.Tag}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</TabContext>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RepoDetails;
|
133
src/components/RepoDetailsMetadata.jsx
Normal file
133
src/components/RepoDetailsMetadata.jsx
Normal file
@ -0,0 +1,133 @@
|
||||
import { Card, CardContent, Grid, Typography } from '@mui/material';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import React from 'react';
|
||||
import transform from '../utilities/transform';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
marginBottom: 2,
|
||||
display:"flex",
|
||||
flexDirection:"row",
|
||||
alignItems:"start",
|
||||
background:"#FFFFFF",
|
||||
boxShadow:"0px 5px 10px rgba(131, 131, 131, 0.08)",
|
||||
borderRadius:"24px",
|
||||
flex:"none",
|
||||
alignSelf:"stretch",
|
||||
flexGrow:0,
|
||||
order:0,
|
||||
width:"100%"
|
||||
},
|
||||
metadataHeader: {
|
||||
color: "rgba(0, 0, 0, 0.6)"
|
||||
},
|
||||
metadataBody: {
|
||||
color: "rgba(0, 0, 0, 0.87)",
|
||||
fontFamily: 'Roboto',
|
||||
fontStyle: "normal",
|
||||
fontWeight: 400,
|
||||
fontSize: "16px",
|
||||
lineHeight: "150%",
|
||||
align:"left"
|
||||
}
|
||||
}));
|
||||
|
||||
function RepoDetailsMetadata (props) {
|
||||
const classes = useStyles();
|
||||
const {name, repoURL, weeklyDownloads, lastUpdated, size, filesNr, latestTag, issues, prs} = props;
|
||||
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid container item xs={12}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Repository</Typography>
|
||||
<Typography variant="body1" className={classes.metadataBody}>{repoURL || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid container item xs={12}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Weekly downloads</Typography>
|
||||
<Typography variant="body1" align="left" className={classes.metadataBody}>{weeklyDownloads || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid container item xs={12} spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Last publish</Typography>
|
||||
<Typography variant="body1" className={classes.metadataBody}>{lastUpdated || `35 days ago`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Image size</Typography>
|
||||
<Typography variant="body1" className={classes.metadataBody}>{transform.formatBytes(size) || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container item xs={12} spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Last publish</Typography>
|
||||
<Typography variant="body1" className={classes.metadataBody}>{lastUpdated || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Image size</Typography>
|
||||
<Typography variant="body1" className={classes.metadataBody}>{size || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container item xs={12} spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Files</Typography>
|
||||
<Typography variant="body1" align="left" className={classes.metadataBody}>{filesNr || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Latest tag</Typography>
|
||||
<Typography variant="body1" align="left" className={classes.metadataBody}>{latestTag || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container item xs={12} spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Issues</Typography>
|
||||
<Typography variant="body1" align="left" className={classes.metadataBody}>{issues || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Card variant="outlined" className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="body2" align="left" className={classes.metadataHeader}>Pull requests</Typography>
|
||||
<Typography variant="body1" align="left" className={classes.metadataBody}>{prs || `----`}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default RepoDetailsMetadata;
|
@ -3,8 +3,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { host } from '../constants';
|
||||
// utility
|
||||
import api from '../api';
|
||||
import { URL } from '../constants';
|
||||
import {api, endpoints} from '../api';
|
||||
|
||||
// components
|
||||
import Button from '@mui/material/Button';
|
||||
@ -80,7 +79,7 @@ export default function SignIn({ username, updateUsername, password, updatePassw
|
||||
}
|
||||
};
|
||||
}
|
||||
api.get(`${host}${URL.imageList}`,cfg)
|
||||
api.get(`${host}${endpoints.imageList}`,cfg)
|
||||
.then(response => {
|
||||
if (response.data && response.data.data) {
|
||||
if(isAuthEnabled) {
|
||||
|
@ -5,102 +5,88 @@ import PropTypes from 'prop-types';
|
||||
// components
|
||||
import Box from '@mui/material/Box';
|
||||
import Collapse from '@mui/material/Collapse';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableCell from '@mui/material/TableCell';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableCell, {tableCellClasses} from '@mui/material/TableCell';
|
||||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
import transform from 'utilities/transform';
|
||||
import { Card, CardContent, Divider } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
marginBottom: 2,
|
||||
display:"flex",
|
||||
flexDirection:"row",
|
||||
alignItems:"center",
|
||||
background:"#FFFFFF",
|
||||
boxShadow:"0px 5px 10px rgba(131, 131, 131, 0.08)",
|
||||
borderRadius:"30px",
|
||||
flex:"none",
|
||||
alignSelf:"stretch",
|
||||
flexGrow:0,
|
||||
order:0,
|
||||
width:"100%"
|
||||
},
|
||||
content: {
|
||||
textAlign: "left",
|
||||
color: "#606060",
|
||||
padding: "2% 3% 2% 3%",
|
||||
width:"100%"
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
// takes raw # of bytes and decimal value to be returned;
|
||||
// returns bytes with nearest human-readable unit
|
||||
function formatBytes(bytes) {
|
||||
if (isNaN(bytes) || bytes === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const DATA_UNITS = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const k = 1000;
|
||||
|
||||
const unitIdx = Math.floor(Math.log10(bytes) / 3); // log10(1000) = 3
|
||||
let value = bytes / Math.pow(k, unitIdx);
|
||||
|
||||
// minimum 2 significant digits
|
||||
value = value < 10 ? value.toPrecision(2) : Math.round(value);
|
||||
|
||||
return value + ' ' + DATA_UNITS[unitIdx];
|
||||
}
|
||||
|
||||
function Row(props) {
|
||||
function TagCard(props) {
|
||||
const {data, row} = props;
|
||||
const tags = data && data.tags;
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
|
||||
<TableCell component="th" scope="row" style={{color: "#696969"}}>
|
||||
<IconButton
|
||||
aria-label="expand row"
|
||||
size="medium"
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
|
||||
</IconButton>
|
||||
{row.Tag}
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<Box sx={{ margin: 1 }}>
|
||||
<Typography variant="h7" gutterBottom component="div">
|
||||
{
|
||||
// Layers
|
||||
}
|
||||
</Typography>
|
||||
<Table size="small" aria-label="purchases">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={{color: "#696969"}}>Size</TableCell>
|
||||
<TableCell style={{color: "#696969"}}>Digest</TableCell>
|
||||
<Card className={classes.card} raised>
|
||||
<CardContent className={classes.content}>
|
||||
<Typography variant="body1" align="left" sx={{color:"#828282"}}>{row.Tag}</Typography>
|
||||
<Typography variant="caption">Last pushed {row.lastUpdated || '----'} by {row.vendor || '----'}</Typography>
|
||||
<Typography sx={{color:"#7C4DFF", cursor:'pointer'}} onClick={() => setOpen(!open)}>{!open? 'See layers' : 'Hide layers'}</Typography>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom component="div">
|
||||
{
|
||||
// Layers
|
||||
}
|
||||
</Typography>
|
||||
<Table size="small" padding="none" sx={{[`& .${tableCellClasses.root}`]: {borderBottom: "none"}}}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={{color: "#696969"}}><Typography variant="body1">Digest</Typography></TableCell>
|
||||
<TableCell style={{color: "#696969"}}><Typography variant="body1">OS/ARCH</Typography></TableCell>
|
||||
<TableCell style={{color: "#696969"}}><Typography variant="body1">Size</Typography></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{row.Layers.map((layer) => (
|
||||
<TableRow key={layer.Digest} onClick={() => {navigator.clipboard.writeText(layer.Digest)}}>
|
||||
<TableCell style={{color: "#696969"}}><Typography variant="body1">{layer.Digest?.substr(0,12)}</Typography></TableCell>
|
||||
<TableCell style={{color: "#696969"}}><Typography variant="body1">-----------</Typography></TableCell>
|
||||
<TableCell component="th" scope="row" style={{color: "#696969"}}>
|
||||
<Typography variant="body1">{transform.formatBytes(layer.Size)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{row.Layers.map((layer) => (
|
||||
<TableRow key={layer.Digest}>
|
||||
<TableCell component="th" scope="row" style={{color: "#696969"}}>
|
||||
{formatBytes(layer.Size)}
|
||||
</TableCell>
|
||||
<TableCell style={{color: "#696969"}}>{layer.Digest}</TableCell>
|
||||
<TableCell style={{color: "#696969"}}>
|
||||
<ContentCopyIcon sx={{height: 16, width: 16}} onClick={() => {navigator.clipboard.writeText(layer.Digest)}} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Row.propTypes = {
|
||||
TagCard.propTypes = {
|
||||
row: PropTypes.shape({
|
||||
calories: PropTypes.number.isRequired,
|
||||
carbs: PropTypes.number.isRequired,
|
||||
fat: PropTypes.number.isRequired,
|
||||
Layers: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
amount: PropTypes.number.isRequired,
|
||||
@ -109,8 +95,6 @@ Row.propTypes = {
|
||||
}),
|
||||
).isRequired,
|
||||
Tag: PropTypes.string.isRequired,
|
||||
price: PropTypes.number.isRequired,
|
||||
protein: PropTypes.number.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
@ -118,7 +102,7 @@ Row.propTypes = {
|
||||
const renderTags = (tags) => {
|
||||
const cmp = tags && tags.map((tag, index) => {
|
||||
return (
|
||||
<Row key={tag.Tag} row={tag} />
|
||||
<TagCard key={tag.Tag} row={tag} />
|
||||
);
|
||||
});
|
||||
return cmp;
|
||||
@ -126,22 +110,17 @@ const renderTags = (tags) => {
|
||||
|
||||
|
||||
export default function CollapsibleTable(props) {
|
||||
const classes = useStyles();
|
||||
const {data} = props;
|
||||
const {tags} = data;
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<Table aria-label="collapsible table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell> <Typography variant="h7" gutterBottom component="div" style={{color: "#696969"}}>Tags</Typography></TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{renderTags(tags)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Card className={classes.card}>
|
||||
<CardContent className={classes.content}>
|
||||
<Typography variant="h4" gutterBottom component="div" align="left" style={{color: "rgba(0, 0, 0, 0.87)"}}>Tags</Typography>
|
||||
<Divider variant="fullWidth" sx={{margin:"5% 0% 5% 0%", background:"rgba(0, 0, 0, 0.38)", height:"1px", width:"100%"}}/>
|
||||
{renderTags(tags)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,3 @@
|
||||
|
||||
const URL = {
|
||||
imageList: '/v2/_zot/ext/search?query={ImageListWithLatestTag () { Name Latest LastUpdated Description Licenses Vendor Size Labels}}',
|
||||
};
|
||||
|
||||
const host = 'http://localhost:5000'
|
||||
|
||||
export {URL, host};
|
||||
export {host};
|
||||
|
51
src/pages/RepoPage.jsx
Normal file
51
src/pages/RepoPage.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
// react global
|
||||
import React from 'react';
|
||||
|
||||
// components
|
||||
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { Container, Grid, Stack } from '@mui/material';
|
||||
import Header from 'components/Header';
|
||||
import RepoDetails from 'components/RepoDetails';
|
||||
import ExploreHeader from 'components/ExploreHeader';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
pageWrapper: {
|
||||
backgroundColor: "#f2f2f2a1",
|
||||
},
|
||||
container: {
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5,
|
||||
backgroundColor: "#f2f2f2a1",
|
||||
},
|
||||
parentWrapper: {
|
||||
height: '100vh',
|
||||
},
|
||||
gridWrapper: {
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
backgroundColor: "#fff",
|
||||
width:"100%",
|
||||
border: "1px #f2f2f2 dashed",
|
||||
},
|
||||
}));
|
||||
|
||||
function RepoPage(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Stack direction="column" className={classes.pageWrapper}>
|
||||
<Header updateKeywords={props.updateKeywords}></Header>
|
||||
<Container className={classes.container} >
|
||||
<ExploreHeader/>
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
<Grid item xs={12}>
|
||||
<RepoDetails />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default RepoPage;
|
5
src/utilities/mockData.js
Normal file
5
src/utilities/mockData.js
Normal file
@ -0,0 +1,5 @@
|
||||
const mockData = {
|
||||
loremIpsum: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. At quam nulla euismod mus sed. Gravida ornare tortor diam nullam donec eget. Sed et cursus tristique leo arcu eget sed. Sit neque morbi praesent tellus duis lectus orci.\n Laoreet sodales aenean libero pharetra tellus. Nisl blandit quis lorem platea. Mauris id neque nec blandit ipsum aliquet venenatis egestas sed. Tempus felis sed aliquam proin aliquet. Elementum egestas sagittis nibh orci, varius interdum pretium risus. Nullam eu id tempus faucibus nullam purus nibh mi.\n Tincidunt pretium vehicula metus a dui est proin. Ullamcorper vitae pulvinar diam habitant a, tellus pellentesque consectetur et. Vel, pellentesque tellus ultrices non molestie. Eu, est dignissim sit vivamus. At orci, urna rhoncus sed ultrices. Dui elit dui vestibulum ipsum sed morbi pellentesque sed lacus."
|
||||
}
|
||||
|
||||
export default mockData;
|
23
src/utilities/transform.js
Normal file
23
src/utilities/transform.js
Normal file
@ -0,0 +1,23 @@
|
||||
const transform = {
|
||||
// takes raw # of bytes and decimal value to be returned;
|
||||
// returns bytes with nearest human-readable unit
|
||||
formatBytes : (bytes) => {
|
||||
if (isNaN(bytes) || bytes === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const DATA_UNITS = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const k = 1000;
|
||||
|
||||
const unitIdx = Math.floor(Math.log10(bytes) / 3); // log10(1000) = 3
|
||||
let value = bytes / Math.pow(k, unitIdx);
|
||||
|
||||
// minimum 2 significant digits
|
||||
// @ts-ignore
|
||||
value = value < 10 ? value.toPrecision(2) : Math.round(value);
|
||||
|
||||
return value + ' ' + DATA_UNITS[unitIdx];
|
||||
}
|
||||
}
|
||||
|
||||
export default transform;
|
Loading…
x
Reference in New Issue
Block a user