diff --git a/docusaurus.config.js b/docusaurus.config.js index 9219274e05..a0b249a232 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -370,6 +370,11 @@ const config = { label: "Download", position: "left", }, + { + href: "/maintainer/", + label: "Maintainer", + position: "right", + }, { href: "https://opencollective.com/conda-forge", label: "Donate", diff --git a/package-lock.json b/package-lock.json index 77bdb7f0fb..0b4fbbd647 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "cf-infra-docs", "version": "0.0.0", "dependencies": { + "@apollo/client": "^3.12.7", "@docusaurus/core": "^3.6.3", "@docusaurus/plugin-client-redirects": "^3.6.3", "@docusaurus/plugin-content-blog": "^3.5.2", @@ -18,6 +19,7 @@ "chart.js": "^4.4.7", "chartjs-adapter-moment": "^1.0.1", "clsx": "^2.1.1", + "graphql": "^16.10.0", "install": "^0.13.0", "moment": "^2.29.4", "octokit": "^4.0.3", @@ -401,6 +403,49 @@ "node": ">=6.0.0" } }, + "node_modules/@apollo/client": { + "version": "3.12.7", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.12.7.tgz", + "integrity": "sha512-c0LSzS3tmJ06WSyNxsTHlfc4OLLYDnWtN+zkRjMQ80KCcp89sEpNgZP5ZCXdt2pUwUqOAvZFKJW7L8tolDzkrw==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@wry/caches": "^1.0.0", + "@wry/equality": "^0.5.6", + "@wry/trie": "^0.5.0", + "graphql-tag": "^2.12.6", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.18.0", + "prop-types": "^15.7.2", + "rehackt": "^0.1.0", + "response-iterator": "^0.2.6", + "symbol-observable": "^4.0.0", + "ts-invariant": "^0.10.3", + "tslib": "^2.3.0", + "zen-observable-ts": "^1.2.5" + }, + "peerDependencies": { + "graphql": "^15.0.0 || ^16.0.0", + "graphql-ws": "^5.5.5", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" + }, + "peerDependenciesMeta": { + "graphql-ws": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "subscriptions-transport-ws": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -3866,6 +3911,15 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz", "integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==" }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -5305,6 +5359,54 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@wry/caches": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", + "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/context": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz", + "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/equality": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz", + "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/trie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", + "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -9115,6 +9217,30 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "node_modules/graphql": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", @@ -13544,6 +13670,18 @@ "opener": "bin/opener-bin.js" } }, + "node_modules/optimism": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz", + "integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==", + "license": "MIT", + "dependencies": { + "@wry/caches": "^1.0.0", + "@wry/context": "^0.7.0", + "@wry/trie": "^0.5.0", + "tslib": "^2.3.0" + } + }, "node_modules/p-cancelable": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", @@ -15982,6 +16120,24 @@ "regjsparser": "bin/parser" } }, + "node_modules/rehackt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", + "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/rehype-raw": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", @@ -16280,6 +16436,54 @@ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" }, + "node_modules/response-iterator": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.19.tgz", + "integrity": "sha512-9SNSciJRoDouZg4ClSfjGVw+nLNs0VD/XNxEUdQIMfNHrgIf2aBYwQicbroY4eg6KQiu2WMclH3kOBnxU3Thzw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.8" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/response-iterator/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/response-iterator/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/response-iterator/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/response-iterator/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/responselike": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", @@ -17193,6 +17397,15 @@ "node": ">= 10" } }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -17409,6 +17622,18 @@ "node": ">=6.10" } }, + "node_modules/ts-invariant": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", + "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -18577,6 +18802,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==", + "license": "MIT" + }, + "node_modules/zen-observable-ts": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz", + "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==", + "license": "MIT", + "dependencies": { + "zen-observable": "0.8.15" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 2573655beb..9ef1371d0d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { + "@apollo/client": "^3.12.7", "@docusaurus/core": "^3.6.3", "@docusaurus/plugin-client-redirects": "^3.6.3", "@docusaurus/preset-classic": "^3.6.3", @@ -24,6 +25,7 @@ "chart.js": "^4.4.7", "chartjs-adapter-moment": "^1.0.1", "clsx": "^2.1.1", + "graphql":"^16.10.0", "install": "^0.13.0", "moment": "^2.29.4", "octokit": "^4.0.3", diff --git a/src/components/MaintainerDashboard/index.jsx b/src/components/MaintainerDashboard/index.jsx new file mode 100644 index 0000000000..411338e839 --- /dev/null +++ b/src/components/MaintainerDashboard/index.jsx @@ -0,0 +1,100 @@ +import { useLocation } from "@docusaurus/router"; +import { useColorMode } from '@docusaurus/theme-common'; +import { urls } from "@site/src/constants"; +import { React, useEffect, useState } from "react"; +import { gql, useApolloClient, useLazyQuery, useQuery } from '@apollo/client'; + +const GET_USER = gql` + query { + viewer { + login + } + } +`; + +const GET_TEAMS = gql` + query GetTeams($login: String!) { + organization(login: "conda-forge") { + teams(first: 100, userLogins: [$login]) { + totalCount + nodes { + name + repositories { + edges { + node { + name + url + isArchived + issues(states: OPEN) { + totalCount + } + pullRequests(states: OPEN) { + totalCount + } + } + } + } + } + } + } + } +`; + +export default function MaintainerDashboard() { + const user = useQuery(GET_USER); + + const [getTeams, teamData] = useLazyQuery(GET_TEAMS); + + useEffect(() => { + if (user?.data?.viewer?.login) { + getTeams({ variables: { login: user.data.viewer.login } }); + } + }, [user]); + + if (teamData.loading || !teamData.called) return 'Loading...'; + + const all_teams = teamData.data.organization.teams.nodes; + const empty_teams = all_teams.filter(team => team.repositories.edges.length === 0); + const overfull_teams = all_teams.filter(team => team.repositories.edges.length > 1); + const regular_teams = all_teams.filter(team => ( + team.repositories.edges.length === 1 && !team.repositories.edges[0].node.isArchived + )); + + const teams = regular_teams.map((team, index) => { + const repository = team.repositories.edges[0].node; + const name = repository.name; + const url = repository.url; + const openIssues = repository.issues.totalCount; + const openPullRequests = repository.pullRequests.totalCount; + return { index, name, url, openPullRequests, openIssues }; + }).sort((a, b) => { + const prOrder = b.openPullRequests - a.openPullRequests; + if (prOrder !== 0) { + return prOrder; + } else { + return b.openIssues - a.openIssues; + } + }); + + return ( +
+ + + + + + + + + + {teams.map(({ index, name, url, openPullRequests, openIssues }) => + ( + + + + ))} + +
TeamOpen pull requestsOpen issues
{name}{openPullRequests}{openIssues}
+
+ ); +} diff --git a/src/pages/maintainer/index.jsx b/src/pages/maintainer/index.jsx new file mode 100644 index 0000000000..8b6c80e3cf --- /dev/null +++ b/src/pages/maintainer/index.jsx @@ -0,0 +1,39 @@ +import React from "react"; +import Layout from "@theme/Layout"; +import MaintainerDashboard from "@site/src/components/MaintainerDashboard"; +import { ApolloClient, createHttpLink, InMemoryCache, ApolloProvider } from '@apollo/client'; +import { setContext } from '@apollo/client/link/context'; + +const httpLink = createHttpLink({ + uri: 'https://api.github.com/graphql', +}); + +const authLink = setContext((_, { headers }) => { + // get the authentication token from local storage if it exists + const token = localStorage.getItem('token'); + // return the headers to the context so httpLink can read them + return { + headers: { + ...headers, + authorization: token ? `Bearer ${token}` : "", + } + } +}); + +const client = new ApolloClient({ + link: authLink.concat(httpLink), + cache: new InMemoryCache(), +}); + +export default function Home() { + return ( + + + + + + ); +}