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 (
+
+
+
+ );
+}
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 (
+
+
+
+
+
+ );
+}