diff --git a/package-lock.json b/package-lock.json index 801b5141..5ebd1426 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@patternfly/react-table": "^6.0.0", "axios": "^1.7.7", "js-yaml": "^4.1.0", - "next": "^14.2.15", + "next": "^15.0.2", "next-auth": "^4.24.10", "node-fetch": "^3.3.2", "react": "18.3.1", @@ -341,6 +341,15 @@ "kuler": "^2.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -560,6 +569,348 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -676,9 +1027,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.15.tgz", - "integrity": "sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.2.tgz", + "integrity": "sha512-GK+8w88z+AFlmt+ondytZo2xpwlfAR8U6CRwXancHImh6EdGfHMIrTSCcx5sOSBei00GyLVL0ioo1JLKTfprgg==", "cpu": [ "arm64" ], @@ -691,9 +1042,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz", - "integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.2.tgz", + "integrity": "sha512-KUpBVxIbjzFiUZhiLIpJiBoelqzQtVZbdNNsehhUn36e2YzKHphnK8eTUW1s/4aPy5kH/UTid8IuVbaOpedhpw==", "cpu": [ "x64" ], @@ -706,9 +1057,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz", - "integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.2.tgz", + "integrity": "sha512-9J7TPEcHNAZvwxXRzOtiUvwtTD+fmuY0l7RErf8Yyc7kMpE47MIQakl+3jecmkhOoIyi/Rp+ddq7j4wG6JDskQ==", "cpu": [ "arm64" ], @@ -721,9 +1072,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz", - "integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.2.tgz", + "integrity": "sha512-BjH4ZSzJIoTTZRh6rG+a/Ry4SW0HlizcPorqNBixBWc3wtQtj4Sn9FnRZe22QqrPnzoaW0ctvSz4FaH4eGKMww==", "cpu": [ "arm64" ], @@ -736,9 +1087,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz", - "integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.2.tgz", + "integrity": "sha512-i3U2TcHgo26sIhcwX/Rshz6avM6nizrZPvrDVDY1bXcLH1ndjbO8zuC7RoHp0NSK7wjJMPYzm7NYL1ksSKFreA==", "cpu": [ "x64" ], @@ -751,9 +1102,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz", - "integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.2.tgz", + "integrity": "sha512-AMfZfSVOIR8fa+TXlAooByEF4OB00wqnms1sJ1v+iu8ivwvtPvnkwdzzFMpsK5jA2S9oNeeQ04egIWVb4QWmtQ==", "cpu": [ "x64" ], @@ -766,9 +1117,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz", - "integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.2.tgz", + "integrity": "sha512-JkXysDT0/hEY47O+Hvs8PbZAeiCQVxKfGtr4GUpNAhlG2E0Mkjibuo8ryGD29Qb5a3IOnKYNoZlh/MyKd2Nbww==", "cpu": [ "arm64" ], @@ -780,25 +1131,10 @@ "node": ">= 10" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz", - "integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz", - "integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.2.tgz", + "integrity": "sha512-foaUL0NqJY/dX0Pi/UcZm5zsmSk5MtP/gxx3xOPyREkMFN+CTjctPfu3QaqrQHinaKdPnMWPJDKt4VjDfTBe/Q==", "cpu": [ "x64" ], @@ -1070,16 +1406,13 @@ "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", - "license": "Apache-2.0", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz", + "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==", "dependencies": { - "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, @@ -1839,8 +2172,7 @@ "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, "node_modules/color": { "version": "3.2.1", @@ -2116,6 +2448,15 @@ "node": ">=0.4.0" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3731,6 +4072,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -4686,40 +5028,41 @@ "license": "MIT" }, "node_modules/next": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.15.tgz", - "integrity": "sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.0.2.tgz", + "integrity": "sha512-rxIWHcAu4gGSDmwsELXacqAPUk+j8dV/A9cDF5fsiCMpkBDYkO2AEaL1dfD+nNmDiU6QMCFN8Q30VEKapT9UHQ==", "dependencies": { - "@next/env": "14.2.15", - "@swc/helpers": "0.5.5", + "@next/env": "15.0.2", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.13", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.6" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=18.17.0" + "node": ">=18.18.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.15", - "@next/swc-darwin-x64": "14.2.15", - "@next/swc-linux-arm64-gnu": "14.2.15", - "@next/swc-linux-arm64-musl": "14.2.15", - "@next/swc-linux-x64-gnu": "14.2.15", - "@next/swc-linux-x64-musl": "14.2.15", - "@next/swc-win32-arm64-msvc": "14.2.15", - "@next/swc-win32-ia32-msvc": "14.2.15", - "@next/swc-win32-x64-msvc": "14.2.15" + "@next/swc-darwin-arm64": "15.0.2", + "@next/swc-darwin-x64": "15.0.2", + "@next/swc-linux-arm64-gnu": "15.0.2", + "@next/swc-linux-arm64-musl": "15.0.2", + "@next/swc-linux-x64-gnu": "15.0.2", + "@next/swc-linux-x64-musl": "15.0.2", + "@next/swc-win32-arm64-msvc": "15.0.2", + "@next/swc-win32-x64-msvc": "15.0.2", + "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-02c0e824-20241028", + "react-dom": "^18.2.0 || 19.0.0-rc-02c0e824-20241028", "sass": "^1.3.0" }, "peerDependenciesMeta": { @@ -4729,6 +5072,9 @@ "@playwright/test": { "optional": true }, + "babel-plugin-react-compiler": { + "optional": true + }, "sass": { "optional": true } @@ -4774,11 +5120,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/next/node_modules/@next/env": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.15.tgz", - "integrity": "sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==" - }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5750,6 +6091,88 @@ "node": ">= 0.4" } }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/sharp/node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/sharp/node_modules/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==", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/sharp/node_modules/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==", + "optional": true + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6083,10 +6506,9 @@ } }, "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "license": "MIT", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "dependencies": { "client-only": "0.0.1" }, @@ -6094,7 +6516,7 @@ "node": ">= 12.0.0" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { diff --git a/package.json b/package.json index d0e7c798..1f60f2fd 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@patternfly/react-table": "^6.0.0", "axios": "^1.7.7", "js-yaml": "^4.1.0", - "next": "^14.2.15", + "next": "^15.0.2", "next-auth": "^4.24.10", "node-fetch": "^3.3.2", "react": "18.3.1", diff --git a/public/default-avatar.png b/public/default-avatar.png new file mode 100644 index 00000000..b87ded01 Binary files /dev/null and b/public/default-avatar.png differ diff --git a/public/default-avatar.svg b/public/default-avatar.svg new file mode 100644 index 00000000..73726f9b --- /dev/null +++ b/public/default-avatar.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/src/app/edit-submission/knowledge/[id]/page.tsx b/src/app/edit-submission/knowledge/[id]/page.tsx index 50e5f678..55b25ce4 100644 --- a/src/app/edit-submission/knowledge/[id]/page.tsx +++ b/src/app/edit-submission/knowledge/[id]/page.tsx @@ -1,179 +1,19 @@ // src/app/edit-submission/knowledge/[id]/page.tsx -'use client'; - import * as React from 'react'; -import { useSession } from 'next-auth/react'; -import { AppLayout } from '../../../../components/AppLayout'; -import { AttributionData, PullRequestFile, KnowledgeYamlData } from '@/types'; -import { KnowledgeSchemaVersion } from '@/types/const'; -import { fetchPullRequest, fetchFileContent, fetchPullRequestFiles } from '../../../../utils/github'; -import yaml from 'js-yaml'; -import axios from 'axios'; -import KnowledgeForm, { KnowledgeEditFormData, KnowledgeFormData, QuestionAndAnswerPair, SeedExample } from '@/components/Contribute/Knowledge'; -import { ValidatedOptions } from '@patternfly/react-core/dist/esm/helpers/constants'; -import { useEffect, useState } from 'react'; -import { Modal, ModalVariant } from '@patternfly/react-core/dist/esm/components/Modal/Modal'; -import { useRouter } from 'next/navigation'; - -const EditKnowledgePage: React.FunctionComponent<{ params: { id: string } }> = ({ params }) => { - const { data: session } = useSession(); - const [isLoading, setIsLoading] = useState(true); - const [loadingMsg, setLoadingMsg] = useState(''); - const [knowledgeEditFormData, setKnowledgeEditFormData] = useState(); - const prNumber = parseInt(params.id, 10); - const router = useRouter(); - - useEffect(() => { - setLoadingMsg('Fetching knowledge data from PR : ' + prNumber); - const fetchPRData = async () => { - if (session?.accessToken) { - try { - const prData = await fetchPullRequest(session.accessToken, prNumber); - - // Create KnowledgeFormData from existing form. - const knowledgeExistingFormData: KnowledgeFormData = { - email: '', - name: '', - submissionSummary: '', - domain: '', - documentOutline: '', - filePath: '', - seedExamples: [], - knowledgeDocumentRepositoryUrl: '', - knowledgeDocumentCommit: '', - documentName: '', - titleWork: '', - linkWork: '', - revision: '', - licenseWork: '', - creators: '' - }; - - const knowledgeEditFormData: KnowledgeEditFormData = { - isEditForm: true, - knowledgeVersion: KnowledgeSchemaVersion, - branchName: '', - knowledgeFormData: knowledgeExistingFormData, - pullRequestNumber: prNumber, - yamlFile: { filename: '' }, - attributionFile: { filename: '' } - }; - - knowledgeExistingFormData.submissionSummary = prData.title; - knowledgeEditFormData.branchName = prData.head.ref; // Store the branch name from the pull request - - const prFiles: PullRequestFile[] = await fetchPullRequestFiles(session.accessToken, prNumber); - - const foundYamlFile = prFiles.find((file: PullRequestFile) => file.filename.endsWith('.yaml')); - if (!foundYamlFile) { - throw new Error('No YAML file found in the pull request.'); - } - knowledgeEditFormData.yamlFile = foundYamlFile; - - const yamlContent = await fetchFileContent(session.accessToken, foundYamlFile.filename, prData.head.sha); - const yamlData: KnowledgeYamlData = yaml.load(yamlContent) as KnowledgeYamlData; - console.log('Parsed YAML data:', yamlData); +import { AppLayout } from '@/components/AppLayout'; +import EditKnowledge from '@/components/Contribute/EditKnowledge/EditKnowledge'; - // Populate the form fields with YAML data - knowledgeExistingFormData.documentOutline = yamlData.document_outline; - knowledgeExistingFormData.domain = yamlData.domain; - knowledgeExistingFormData.knowledgeDocumentRepositoryUrl = yamlData.document.repo; - knowledgeExistingFormData.knowledgeDocumentCommit = yamlData.document.commit; - knowledgeExistingFormData.documentName = yamlData.document.patterns.join(', '); - - const seedExamples: SeedExample[] = []; - yamlData.seed_examples.forEach((seed, index) => { - // iterate through questions_and_answers and create a new object for each - const example: SeedExample = { - immutable: index < 5 ? true : false, - isExpanded: true, - context: seed.context, - isContextValid: ValidatedOptions.success, - questionAndAnswers: [] - }; - - const qnaExamples: QuestionAndAnswerPair[] = seed.questions_and_answers.map((qa, index) => { - const qna: QuestionAndAnswerPair = { - question: qa.question, - answer: qa.answer, - immutable: index < 3 ? true : false, - isQuestionValid: ValidatedOptions.success, - isAnswerValid: ValidatedOptions.success - }; - return qna; - }); - example.questionAndAnswers = qnaExamples; - seedExamples.push(example); - }); - knowledgeExistingFormData.seedExamples = seedExamples; - - // Set the file path from the current YAML file (remove the root folder name from the path) - const currentFilePath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); - knowledgeEditFormData.knowledgeFormData.filePath = currentFilePath + '/'; - - // Fetch and parse attribution file if it exists - const foundAttributionFile = prFiles.find((file: PullRequestFile) => file.filename.includes('attribution')); - if (foundAttributionFile) { - const attributionContent = await fetchFileContent(session.accessToken, foundAttributionFile.filename, prData.head.sha); - const attributionData = parseAttributionContent(attributionContent); - console.log('Parsed attribution data:', attributionData); - - knowledgeEditFormData.attributionFile = foundAttributionFile; - // Populate the form fields with attribution data - knowledgeExistingFormData.titleWork = attributionData.title_of_work; - knowledgeExistingFormData.linkWork = attributionData.link_to_work ? attributionData.link_to_work : ''; - knowledgeExistingFormData.revision = attributionData.revision ? attributionData.revision : ''; - knowledgeExistingFormData.licenseWork = attributionData.license_of_the_work; - knowledgeExistingFormData.creators = attributionData.creator_names; - } - setKnowledgeEditFormData(knowledgeEditFormData); - setIsLoading(false); - } catch (error) { - if (axios.isAxiosError(error)) { - console.error('Error fetching pull request data:', error.response ? error.response.data : error.message); - setLoadingMsg('Error fetching knowledge data from PR : ' + prNumber) + '. Please try again.'; - } else if (error instanceof Error) { - console.error('Error fetching pull request data:', error.message); - setLoadingMsg('Error fetching knowledge data from PR : ' + prNumber + ' [' + error.message + ']') + '. Please try again.'; - } - } - } - }; - fetchPRData(); - }, [session, prNumber]); - - const parseAttributionContent = (content: string): AttributionData => { - const lines = content.split('\n'); - const attributionData: { [key: string]: string } = {}; - lines.forEach((line) => { - const [key, ...value] = line.split(':'); - if (key && value) { - // Remove spaces in the attribution field for parsing - const normalizedKey = key.trim().toLowerCase().replace(/\s+/g, '_'); - attributionData[normalizedKey] = value.join(':').trim(); - } - }); - return attributionData as unknown as AttributionData; - }; - - const handleOnClose = () => { - router.push('/dashboard'); - setIsLoading(false); - }; +type PageProps = { + params: Promise<{ id: string }>; +}; - if (isLoading) { - return ( - - handleOnClose()}> -
{loadingMsg}
-
-
- ); - } +const EditKnowledgePage = async ({ params }: PageProps) => { + const resolvedParams = await params; + const prNumber = parseInt(resolvedParams.id, 10); return ( - + ); }; diff --git a/src/app/edit-submission/skill/[id]/page.tsx b/src/app/edit-submission/skill/[id]/page.tsx index 9feae61c..735436fa 100644 --- a/src/app/edit-submission/skill/[id]/page.tsx +++ b/src/app/edit-submission/skill/[id]/page.tsx @@ -1,158 +1,19 @@ -// src/app/edit-submission/knowledge/[id]/page.tsx -'use client'; - +// src/app/edit-submission/skill/[id]/page.tsx import * as React from 'react'; -import { useSession } from 'next-auth/react'; -import { AppLayout } from '../../../../components/AppLayout'; -import { AttributionData, PullRequestFile, SkillYamlData } from '@/types'; -import { SkillSchemaVersion } from '@/types/const'; -import { fetchPullRequest, fetchFileContent, fetchPullRequestFiles } from '../../../../utils/github'; -import yaml from 'js-yaml'; -import axios from 'axios'; -import SkillForm, { SkillEditFormData, SkillFormData, SeedExample } from '@/components/Contribute/Skill'; -import { ValidatedOptions } from '@patternfly/react-core/dist/esm/helpers/constants'; -import { useEffect, useState } from 'react'; -import { Modal, ModalVariant } from '@patternfly/react-core/dist/esm/components/Modal/Modal'; -import { useRouter } from 'next/navigation'; - -const EditSkillPage: React.FunctionComponent<{ params: { id: string } }> = ({ params }) => { - const { data: session } = useSession(); - const [isLoading, setIsLoading] = useState(true); - const [loadingMsg, setLoadingMsg] = useState(''); - const [skillEditFormData, setSkillEditFormData] = useState(); - const prNumber = parseInt(params.id, 10); - const router = useRouter(); - - useEffect(() => { - setLoadingMsg('Fetching skill data from PR : ' + prNumber); - const fetchPRData = async () => { - if (session?.accessToken) { - try { - const prData = await fetchPullRequest(session.accessToken, prNumber); - - // Create SkillFormData from existing form. - const skillExistingFormData: SkillFormData = { - email: '', - name: '', - submissionSummary: '', - documentOutline: '', - filePath: '', - seedExamples: [], - titleWork: '', - licenseWork: '', - creators: '' - }; - - const skillEditFormData: SkillEditFormData = { - isEditForm: true, - skillVersion: SkillSchemaVersion, - branchName: '', - skillFormData: skillExistingFormData, - pullRequestNumber: prNumber, - yamlFile: { filename: '' }, - attributionFile: { filename: '' } - }; - - skillExistingFormData.submissionSummary = prData.title; - skillEditFormData.branchName = prData.head.ref; // Store the branch name from the pull request - - const prFiles: PullRequestFile[] = await fetchPullRequestFiles(session.accessToken, prNumber); - - const foundYamlFile = prFiles.find((file: PullRequestFile) => file.filename.endsWith('.yaml')); - if (!foundYamlFile) { - throw new Error('No YAML file found in the pull request.'); - } - skillEditFormData.yamlFile = foundYamlFile; +import { AppLayout } from '@/components/AppLayout'; +import EditSkill from '@/components/Contribute/EditSkill/EditSkill'; - const yamlContent = await fetchFileContent(session.accessToken, foundYamlFile.filename, prData.head.sha); - const yamlData: SkillYamlData = yaml.load(yamlContent) as SkillYamlData; - console.log('Parsed YAML data:', yamlData); - - // Populate the form fields with YAML data - skillExistingFormData.documentOutline = yamlData.task_description; - - const seedExamples: SeedExample[] = []; - yamlData.seed_examples.forEach((seed, index) => { - // iterate through seed examples and create a new object for each - const example: SeedExample = { - immutable: index < 5 ? true : false, - isExpanded: true, - context: seed.context ? seed.context : '', - isContextValid: ValidatedOptions.success, - question: seed.question, - isQuestionValid: ValidatedOptions.success, - answer: seed.answer, - isAnswerValid: ValidatedOptions.success - }; - seedExamples.push(example); - }); - skillExistingFormData.seedExamples = seedExamples; - - // Set the file path from the current YAML file (Note: skills root directory starts from the repo root) - const currentFilePath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); - skillEditFormData.skillFormData.filePath = currentFilePath + '/'; - - // Fetch and parse attribution file if it exists - const foundAttributionFile = prFiles.find((file: PullRequestFile) => file.filename.includes('attribution')); - if (foundAttributionFile) { - const attributionContent = await fetchFileContent(session.accessToken, foundAttributionFile.filename, prData.head.sha); - const attributionData = parseAttributionContent(attributionContent); - console.log('Parsed attribution data:', attributionData); - - skillEditFormData.attributionFile = foundAttributionFile; - // Populate the form fields with attribution data - skillExistingFormData.titleWork = attributionData.title_of_work; - skillExistingFormData.licenseWork = attributionData.license_of_the_work; - skillExistingFormData.creators = attributionData.creator_names; - } - setSkillEditFormData(skillEditFormData); - setIsLoading(false); - } catch (error) { - if (axios.isAxiosError(error)) { - console.error('Error fetching pull request data:', error.response ? error.response.data : error.message); - setLoadingMsg('Error fetching skills data from PR : ' + prNumber) + '. Please try again.'; - } else if (error instanceof Error) { - console.error('Error fetching pull request data:', error.message); - setLoadingMsg('Error fetching skills data from PR : ' + prNumber + ' [' + error.message + ']') + '. Please try again.'; - } - } - } - }; - fetchPRData(); - }, [session, prNumber]); - - const parseAttributionContent = (content: string): AttributionData => { - const lines = content.split('\n'); - const attributionData: { [key: string]: string } = {}; - lines.forEach((line) => { - const [key, ...value] = line.split(':'); - if (key && value) { - // Remove spaces in the attribution field for parsing - const normalizedKey = key.trim().toLowerCase().replace(/\s+/g, '_'); - attributionData[normalizedKey] = value.join(':').trim(); - } - }); - return attributionData as unknown as AttributionData; - }; - - const handleOnClose = () => { - router.push('/dashboard'); - setIsLoading(false); - }; +type PageProps = { + params: Promise<{ id: string }>; +}; - if (isLoading) { - return ( - - handleOnClose()}> -
{loadingMsg}
-
-
- ); - } +const EditSkillPage = async ({ params }: PageProps) => { + const resolvedParams = await params; + const prNumber = parseInt(resolvedParams.id, 10); return ( - + ); }; diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 36af9001..fe59a690 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -14,17 +14,13 @@ const Login: React.FunctionComponent = () => { const [password, setPassword] = useState(''); const [isValidPassword, setIsValidPassword] = useState(true); const [isRememberMeChecked, setIsRememberMeChecked] = useState(false); - const [isProd, setIsProd] = useState(true); + const [isProd, setIsProd] = useState(null); // Use null for initial load state React.useEffect(() => { const chooseLoginPage = async () => { const res = await fetch('/api/envConfig'); const envConfig = await res.json(); - if (envConfig.DEPLOYMENT_TYPE === 'dev') { - setIsProd(false); - } else { - setIsProd(true); - } + setIsProd(envConfig.DEPLOYMENT_TYPE !== 'dev'); }; chooseLoginPage(); }, []); @@ -103,12 +99,15 @@ const Login: React.FunctionComponent = () => { /> ); + if (isProd === null) return null; // Render nothing until environment is loaded + if (isProd) { return ; } return ( = ({ prNumber }) => { + const { data: session } = useSession(); + const [isLoading, setIsLoading] = useState(true); + const [loadingMsg, setLoadingMsg] = useState(''); + const [knowledgeEditFormData, setKnowledgeEditFormData] = useState(); + const router = useRouter(); + + useEffect(() => { + setLoadingMsg('Fetching knowledge data from PR : ' + prNumber); + const fetchPRData = async () => { + if (session?.accessToken) { + try { + const prData = await fetchPullRequest(session.accessToken, prNumber); + + // Create KnowledgeFormData from existing form. + const knowledgeExistingFormData: KnowledgeFormData = { + email: '', + name: '', + submissionSummary: '', + domain: '', + documentOutline: '', + filePath: '', + seedExamples: [], + knowledgeDocumentRepositoryUrl: '', + knowledgeDocumentCommit: '', + documentName: '', + titleWork: '', + linkWork: '', + revision: '', + licenseWork: '', + creators: '' + }; + + const knowledgeEditFormData: KnowledgeEditFormData = { + isEditForm: true, + knowledgeVersion: KnowledgeSchemaVersion, + branchName: '', + knowledgeFormData: knowledgeExistingFormData, + pullRequestNumber: prNumber, + yamlFile: { filename: '' }, + attributionFile: { filename: '' } + }; + + knowledgeExistingFormData.submissionSummary = prData.title; + knowledgeEditFormData.branchName = prData.head.ref; // Store the branch name from the pull request + + const prFiles: PullRequestFile[] = await fetchPullRequestFiles(session.accessToken, prNumber); + + const foundYamlFile = prFiles.find((file: PullRequestFile) => file.filename.endsWith('.yaml')); + if (!foundYamlFile) { + throw new Error('No YAML file found in the pull request.'); + } + knowledgeEditFormData.yamlFile = foundYamlFile; + + const yamlContent = await fetchFileContent(session.accessToken, foundYamlFile.filename, prData.head.sha); + const yamlData: KnowledgeYamlData = yaml.load(yamlContent) as KnowledgeYamlData; + console.log('Parsed YAML data:', yamlData); + + // Populate the form fields with YAML data + knowledgeExistingFormData.documentOutline = yamlData.document_outline; + knowledgeExistingFormData.domain = yamlData.domain; + knowledgeExistingFormData.knowledgeDocumentRepositoryUrl = yamlData.document.repo; + knowledgeExistingFormData.knowledgeDocumentCommit = yamlData.document.commit; + knowledgeExistingFormData.documentName = yamlData.document.patterns.join(', '); + + const seedExamples: SeedExample[] = []; + yamlData.seed_examples.forEach((seed, index) => { + // iterate through questions_and_answers and create a new object for each + const example: SeedExample = { + immutable: index < 5 ? true : false, + isExpanded: true, + context: seed.context, + isContextValid: ValidatedOptions.success, + questionAndAnswers: [] + }; + + const qnaExamples: QuestionAndAnswerPair[] = seed.questions_and_answers.map((qa, index) => { + const qna: QuestionAndAnswerPair = { + question: qa.question, + answer: qa.answer, + immutable: index < 3 ? true : false, + isQuestionValid: ValidatedOptions.success, + isAnswerValid: ValidatedOptions.success + }; + return qna; + }); + example.questionAndAnswers = qnaExamples; + seedExamples.push(example); + }); + knowledgeExistingFormData.seedExamples = seedExamples; + + // Set the file path from the current YAML file (remove the root folder name from the path) + const currentFilePath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); + knowledgeEditFormData.knowledgeFormData.filePath = currentFilePath + '/'; + + // Fetch and parse attribution file if it exists + const foundAttributionFile = prFiles.find((file: PullRequestFile) => file.filename.includes('attribution')); + if (foundAttributionFile) { + const attributionContent = await fetchFileContent(session.accessToken, foundAttributionFile.filename, prData.head.sha); + const attributionData = parseAttributionContent(attributionContent); + console.log('Parsed attribution data:', attributionData); + + knowledgeEditFormData.attributionFile = foundAttributionFile; + // Populate the form fields with attribution data + knowledgeExistingFormData.titleWork = attributionData.title_of_work; + knowledgeExistingFormData.linkWork = attributionData.link_to_work ? attributionData.link_to_work : ''; + knowledgeExistingFormData.revision = attributionData.revision ? attributionData.revision : ''; + knowledgeExistingFormData.licenseWork = attributionData.license_of_the_work; + knowledgeExistingFormData.creators = attributionData.creator_names; + } + setKnowledgeEditFormData(knowledgeEditFormData); + setIsLoading(false); + } catch (error) { + if (axios.isAxiosError(error)) { + console.error('Error fetching pull request data:', error.response ? error.response.data : error.message); + setLoadingMsg('Error fetching knowledge data from PR : ' + prNumber) + '. Please try again.'; + } else if (error instanceof Error) { + console.error('Error fetching pull request data:', error.message); + setLoadingMsg('Error fetching knowledge data from PR : ' + prNumber + ' [' + error.message + ']') + '. Please try again.'; + } + } + } + }; + fetchPRData(); + }, [session, prNumber]); + + const parseAttributionContent = (content: string): AttributionData => { + const lines = content.split('\n'); + const attributionData: { [key: string]: string } = {}; + lines.forEach((line) => { + const [key, ...value] = line.split(':'); + if (key && value) { + // Remove spaces in the attribution field for parsing + const normalizedKey = key.trim().toLowerCase().replace(/\s+/g, '_'); + attributionData[normalizedKey] = value.join(':').trim(); + } + }); + return attributionData as unknown as AttributionData; + }; + + const handleOnClose = () => { + router.push('/dashboard'); + setIsLoading(false); + }; + + if (isLoading) { + return ( + // + handleOnClose()}> +
{loadingMsg}
+
+ //
+ ); + } + + return ( + // + + // + ); +}; + +export default EditKnowledge; diff --git a/src/components/Contribute/EditSkill/EditSkill.tsx b/src/components/Contribute/EditSkill/EditSkill.tsx new file mode 100644 index 00000000..d09cd9c8 --- /dev/null +++ b/src/components/Contribute/EditSkill/EditSkill.tsx @@ -0,0 +1,153 @@ +// src/app/edit-submission/skill/[id]/EditSkillClientComponent.tsx +'use client'; + +import * as React from 'react'; +import { useSession } from 'next-auth/react'; +import { useEffect, useState } from 'react'; +import { ValidatedOptions } from '@patternfly/react-core/dist/esm/helpers/constants'; +import { Modal, ModalVariant } from '@patternfly/react-core/dist/esm/components/Modal/Modal'; +import { useRouter } from 'next/navigation'; +import SkillForm, { SkillEditFormData, SkillFormData, SeedExample } from '@/components/Contribute/Skill'; +import { fetchPullRequest, fetchFileContent, fetchPullRequestFiles } from '@/utils/github'; +import yaml from 'js-yaml'; +import axios from 'axios'; +import { SkillYamlData, AttributionData, PullRequestFile } from '@/types'; +import { SkillSchemaVersion } from '@/types/const'; + +interface EditSkillClientComponentProps { + prNumber: number; +} + +const EditSkill: React.FC = ({ prNumber }) => { + const { data: session } = useSession(); + const [isLoading, setIsLoading] = useState(true); + const [loadingMsg, setLoadingMsg] = useState(''); + const [skillEditFormData, setSkillEditFormData] = useState(); + const router = useRouter(); + + useEffect(() => { + const fetchPRData = async () => { + setLoadingMsg('Fetching skill data from PR: ' + prNumber); + if (session?.accessToken) { + try { + const prData = await fetchPullRequest(session.accessToken, prNumber); + + const skillExistingFormData: SkillFormData = { + email: '', + name: '', + submissionSummary: '', + documentOutline: '', + filePath: '', + seedExamples: [], + titleWork: '', + licenseWork: '', + creators: '' + }; + + const skillEditFormData: SkillEditFormData = { + isEditForm: true, + skillVersion: SkillSchemaVersion, + branchName: '', + skillFormData: skillExistingFormData, + pullRequestNumber: prNumber, + yamlFile: { filename: '' }, + attributionFile: { filename: '' } + }; + + skillExistingFormData.submissionSummary = prData.title; + skillEditFormData.branchName = prData.head.ref; // Store the branch name from the pull request + + const prFiles: PullRequestFile[] = await fetchPullRequestFiles(session.accessToken, prNumber); + + const foundYamlFile = prFiles.find((file: PullRequestFile) => file.filename.endsWith('.yaml')); + if (!foundYamlFile) { + throw new Error('No YAML file found in the pull request.'); + } + skillEditFormData.yamlFile = foundYamlFile; + + const yamlContent = await fetchFileContent(session.accessToken, foundYamlFile.filename, prData.head.sha); + const yamlData: SkillYamlData = yaml.load(yamlContent) as SkillYamlData; + console.log('Parsed YAML data:', yamlData); + + // Populate the form fields with YAML data + skillExistingFormData.documentOutline = yamlData.task_description; + + const seedExamples: SeedExample[] = []; + yamlData.seed_examples.forEach((seed, index) => { + const example: SeedExample = { + immutable: index < 5 ? true : false, + isExpanded: true, + context: seed.context || '', + isContextValid: ValidatedOptions.success, + question: seed.question, + isQuestionValid: ValidatedOptions.success, + answer: seed.answer, + isAnswerValid: ValidatedOptions.success + }; + seedExamples.push(example); + }); + skillExistingFormData.seedExamples = seedExamples; + + // Set the file path from the current YAML file + const currentFilePath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); + skillEditFormData.skillFormData.filePath = currentFilePath + '/'; + + // Fetch and parse attribution file if it exists + const foundAttributionFile = prFiles.find((file: PullRequestFile) => file.filename.includes('attribution')); + if (foundAttributionFile) { + const attributionContent = await fetchFileContent(session.accessToken, foundAttributionFile.filename, prData.head.sha); + const attributionData = parseAttributionContent(attributionContent); + console.log('Parsed attribution data:', attributionData); + + skillEditFormData.attributionFile = foundAttributionFile; + // Populate the form fields with attribution data + skillExistingFormData.titleWork = attributionData.title_of_work; + skillExistingFormData.licenseWork = attributionData.license_of_the_work; + skillExistingFormData.creators = attributionData.creator_names; + } + setSkillEditFormData(skillEditFormData); + setIsLoading(false); + } catch (error) { + if (axios.isAxiosError(error)) { + console.error('Error fetching pull request data:', error.response ? error.response.data : error.message); + setLoadingMsg('Error fetching skills data from PR: ' + prNumber + '. Please try again.'); + } else if (error instanceof Error) { + console.error('Error fetching pull request data:', error.message); + setLoadingMsg('Error fetching skills data from PR: ' + prNumber + ' [' + error.message + ']. Please try again.'); + } + } + } + }; + fetchPRData(); + }, [session, prNumber]); + + const parseAttributionContent = (content: string): AttributionData => { + const lines = content.split('\n'); + const attributionData: { [key: string]: string } = {}; + lines.forEach((line) => { + const [key, ...value] = line.split(':'); + if (key && value) { + const normalizedKey = key.trim().toLowerCase().replace(/\s+/g, '_'); + attributionData[normalizedKey] = value.join(':').trim(); + } + }); + return attributionData as unknown as AttributionData; + }; + + const handleOnClose = () => { + router.push('/dashboard'); + setIsLoading(false); + }; + + if (isLoading) { + return ( + +
{loadingMsg}
+
+ ); + } + + return ; +}; + +export default EditSkill; diff --git a/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx b/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx index 8dd1d0af..bd1280bc 100644 --- a/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx +++ b/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx @@ -85,10 +85,10 @@ export const ViewDropdown: React.FunctionComponent = ({ knowledgeFormData shouldFocusToggleOnSelect > - + YAML Content - + Attribution Content diff --git a/src/components/Contribute/Knowledge/knowledge.css b/src/components/Contribute/Knowledge/knowledge.css index 48a2273d..9f65e32c 100644 --- a/src/components/Contribute/Knowledge/knowledge.css +++ b/src/components/Contribute/Knowledge/knowledge.css @@ -23,7 +23,7 @@ border-color: #45a049; } -.spinner-container{ +.spinner-container { display: flex; justify-content: center; align-items: center; diff --git a/src/components/Contribute/Skill/ViewDropdown/ViewDropdown.tsx b/src/components/Contribute/Skill/ViewDropdown/ViewDropdown.tsx index 29c81e07..14af20e5 100644 --- a/src/components/Contribute/Skill/ViewDropdown/ViewDropdown.tsx +++ b/src/components/Contribute/Skill/ViewDropdown/ViewDropdown.tsx @@ -76,10 +76,10 @@ export const ViewDropdown: React.FunctionComponent = ({ skillFormData, gi shouldFocusToggleOnSelect > - + YAML Content - + Attribution Content diff --git a/src/components/PathService/PathService.tsx b/src/components/PathService/PathService.tsx index 7e265d9a..47d04df0 100644 --- a/src/components/PathService/PathService.tsx +++ b/src/components/PathService/PathService.tsx @@ -1,4 +1,4 @@ -// components/PathService.tsx +// /src/components/PathService.tsx import React, { useState, useEffect, useRef } from 'react'; import { SearchInput } from '@patternfly/react-core/dist/dynamic/components/SearchInput'; import { List } from '@patternfly/react-core/dist/dynamic/components/List'; @@ -44,7 +44,7 @@ const PathService: React.FC = ({ reset, rootPath, path, handle }); if (!response.ok) { - throw new Error('Failed to get tree for subpath ( ' + subpath + ' ) from server.'); + console.warn('Failed to get path service tree for subpath ( ' + subpath + ' ) from server.'); } const result = await response.json(); @@ -55,7 +55,7 @@ const PathService: React.FC = ({ reset, rootPath, path, handle } setItems(result.data.map((item: string) => item.valueOf())); } catch (error) { - console.error('Error fetching data:', error); + console.warn('Error fetching path service data:', error); setItems([]); } }; diff --git a/src/components/UserMenu/UserMenu.tsx b/src/components/UserMenu/UserMenu.tsx index 8fe4e551..7defe55f 100644 --- a/src/components/UserMenu/UserMenu.tsx +++ b/src/components/UserMenu/UserMenu.tsx @@ -17,10 +17,10 @@ const UserMenu: React.FC = () => { useEffect(() => { if (session?.user?.name === 'Admin') { setUserName(session?.user?.name); - setUserImage('https://www.patternfly.org/images/668560cd.svg'); + setUserImage('/default-avatar.png'); } else { setUserName(session?.user?.name ?? ''); - setUserImage(session?.user?.image ?? ''); + setUserImage(session?.user?.image || ''); } }, [session?.user?.name, session?.user?.image]); @@ -40,9 +40,7 @@ const UserMenu: React.FC = () => { toggle={(toggleRef: React.Ref) => ( - - - + {userImage ? : } {userName}