From 42cb52dc69839c5a85491c936b6170824688cc76 Mon Sep 17 00:00:00 2001 From: Itai Gilo Date: Tue, 17 Sep 2024 16:15:36 +0300 Subject: [PATCH 1/5] Add UI for List Pull Requests --- webui/package-lock.json | 15 ++ webui/package.json | 1 + webui/src/lib/api/index.js | 59 ++++++- .../src/lib/components/repository/changes.jsx | 2 +- webui/src/lib/components/repository/tabs.jsx | 8 +- webui/src/pages/index.jsx | 2 + .../repositories/repository/commits/index.jsx | 2 +- .../repositories/repository/pulls/pulls.jsx | 165 ++++++++++++++++++ .../pages/repositories/repository/tags.jsx | 2 +- webui/src/styles/globals.css | 14 +- 10 files changed, 261 insertions(+), 9 deletions(-) create mode 100644 webui/src/pages/repositories/repository/pulls/pulls.jsx diff --git a/webui/package-lock.json b/webui/package-lock.json index 7fb0a296116..b350c26d5d6 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -31,6 +31,7 @@ "react-router-dom": "^6.20.1", "react-simple-code-editor": "^0.13.1", "react-syntax-highlighter": "^15.5.0", + "react-timeago": "^7.2.0", "react-toggle-dark-mode": "^1.1.1", "rehype-raw": "^7.0.0", "rehype-react": "^8.0.0", @@ -20214,6 +20215,14 @@ "node": "*" } }, + "node_modules/react-timeago": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-timeago/-/react-timeago-7.2.0.tgz", + "integrity": "sha512-2KsBEEs+qRhKx/kekUVNSTIpop3Jwd7SRBm0R4Eiq3mPeswRGSsftY9FpKsE/lXLdURyQFiHeHFrIUxLYskG5g==", + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-toggle-dark-mode": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/react-toggle-dark-mode/-/react-toggle-dark-mode-1.1.1.tgz", @@ -38269,6 +38278,12 @@ } } }, + "react-timeago": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-timeago/-/react-timeago-7.2.0.tgz", + "integrity": "sha512-2KsBEEs+qRhKx/kekUVNSTIpop3Jwd7SRBm0R4Eiq3mPeswRGSsftY9FpKsE/lXLdURyQFiHeHFrIUxLYskG5g==", + "requires": {} + }, "react-toggle-dark-mode": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/react-toggle-dark-mode/-/react-toggle-dark-mode-1.1.1.tgz", diff --git a/webui/package.json b/webui/package.json index 84a197d631e..370b62cdc08 100644 --- a/webui/package.json +++ b/webui/package.json @@ -41,6 +41,7 @@ "react-router-dom": "^6.20.1", "react-simple-code-editor": "^0.13.1", "react-syntax-highlighter": "^15.5.0", + "react-timeago": "^7.2.0", "react-toggle-dark-mode": "^1.1.1", "rehype-raw": "^7.0.0", "rehype-react": "^8.0.0", diff --git a/webui/src/lib/api/index.js b/webui/src/lib/api/index.js index 46d9fc2db70..c7da8d3ca11 100644 --- a/webui/src/lib/api/index.js +++ b/webui/src/lib/api/index.js @@ -567,6 +567,62 @@ class Tags { } +class Pulls { + async list(repoId, state = "open", prefix = "", after = "", amount = DEFAULT_LISTING_AMOUNT) { + // const query = qs({prefix, after, amount}); + // const response = await apiRequest(`/repositories/${encodeURIComponent(repoId)}/pulls?` + query); + // if (response.status !== 200) { + // throw new Error(`could not list pulls: ${await extractError(response)}`); + // } + // return response.json(); + + // TODO: this is for development purposes only + console.log("list pulls", {repoId, state, prefix, after, amount}) + let results = [ + { + "id": "test-pull-1", + "title": "Test PR 1", + "status": "open", + "created_at": 1726575741, + "author": "test-user-1", + "description": "This is a test PR", + "source_branch": "feature-branch-1", + "destination_branch": "main" + }, + { + "id": "test-pull-2", + "title": "Next Test PR 2", + "status": "closed", + "created_at": 1726402941, + "author": "test-user-2", + "description": "This is a another test PR", + "source_branch": "feature-branch-2", + "destination_branch": "main" + }, + { + "id": "test-pull-3", + "title": "Another Test PR 3", + "status": "open", + "created_at": 1718454141, + "author": "test-user-1", + "description": "This is also a test PR", + "source_branch": "feature-branch-3", + "destination_branch": "feature-branch-1" + } + ]; + results = results.filter(pull => pull.status === state); + return { + "pagination": { + "has_more": false, + "max_per_page": 1000, + "next_offset": "", + "results": results.length + }, + "results": results + } + } +} + // uploadWithProgress uses good ol' XMLHttpRequest because progress indication in fetch() is // still not well supported across browsers (see https://stackoverflow.com/questions/35711724/upload-progress-indicators-for-fetch). export const uploadWithProgress = (url, file, method = 'POST', onProgress = null, additionalHeaders = null) => { @@ -652,7 +708,7 @@ class Objects { if (response.status === 401) { return false; } - + // This is not one of the expected responses throw new Error(await extractError(response)); } @@ -1112,6 +1168,7 @@ class Import { export const repositories = new Repositories(); export const branches = new Branches(); export const tags = new Tags(); +export const pulls = new Pulls(); export const objects = new Objects(); export const commits = new Commits(); export const refs = new Refs(); diff --git a/webui/src/lib/components/repository/changes.jsx b/webui/src/lib/components/repository/changes.jsx index fdc725c65ee..014c750d9f2 100644 --- a/webui/src/lib/components/repository/changes.jsx +++ b/webui/src/lib/components/repository/changes.jsx @@ -153,7 +153,7 @@ export const ChangesTreeContainer = ({results, delimiter, uriNavigator,
{changesTreeMessage}
- + {(delimiter !== "") && uriNavigator} diff --git a/webui/src/lib/components/repository/tabs.jsx b/webui/src/lib/components/repository/tabs.jsx index a5155e0ac27..995481b9f3d 100644 --- a/webui/src/lib/components/repository/tabs.jsx +++ b/webui/src/lib/components/repository/tabs.jsx @@ -8,7 +8,8 @@ import {Link, NavItem} from "../nav"; import {useRouter} from "../../hooks/router"; import {RefTypeBranch} from "../../../constants"; - +// TODO: this is temp, until PRfD will be ready +const showPulls = false; export const RepositoryNavTabs = ({ active }) => { const { reference } = useRefs(); @@ -59,6 +60,11 @@ export const RepositoryNavTabs = ({ active }) => { Tags + {showPulls && + + Pull Requests + + } Compare diff --git a/webui/src/pages/index.jsx b/webui/src/pages/index.jsx index 14048eec771..1e5af414572 100644 --- a/webui/src/pages/index.jsx +++ b/webui/src/pages/index.jsx @@ -18,6 +18,7 @@ import RepositoryCommitsPage from "./repositories/repository/commits"; import RepositoryCommitPage from "./repositories/repository/commits/commit"; import RepositoryBranchesPage from "./repositories/repository/branches"; import RepositoryTagsPage from "./repositories/repository/tags"; +import RepositoryPullsPage from "./repositories/repository/pulls/pulls"; import RepositoryComparePage from "./repositories/repository/compare"; import RepositoryActionsPage from "./repositories/repository/actions"; import RepositoryGeneralSettingsPage from "./repositories/repository/settings/general"; @@ -62,6 +63,7 @@ export const IndexPage = () => { }/> }/> + }/> }/> }/> diff --git a/webui/src/pages/repositories/repository/commits/index.jsx b/webui/src/pages/repositories/repository/commits/index.jsx index 4152323e9fd..5a42b0b1ea9 100644 --- a/webui/src/pages/repositories/repository/commits/index.jsx +++ b/webui/src/pages/repositories/repository/commits/index.jsx @@ -34,7 +34,7 @@ const CommitWidget = ({ repo, commit }) => { return (
-
+
{ + const authorLink = + + {pull.author} + ; + return ( + +
+
+ + {pull.id} + +
+ Opened ago by {authorLink} +
+
+
+ + + +
+
+
+ ); +}; + +const PullStatus = { + open: "open", + closed: "closed", + merged: "merged", +} + +const PullsList = ({repo, after, prefix, onPaginate}) => { + const router = useRouter() + const [refresh, setRefresh] = useState(true); + // TODO: pullState should be persistent in the url and saved as a url param? + const [pullsState, setPullsState] = useState(PullStatus.open); + const {results, error, loading, nextPage} = useAPIWithPagination(async () => { + return pulls.list(repo.id, pullsState, prefix, after); + }, [repo.id, pullsState, prefix, refresh, after]); + + const doRefresh = () => setRefresh(true); + + let content; + + if (loading) content = ; + else if (error) content = ; + else content = (results && !!results.length ? + <> + + + {results.map(pull => ( + + ))} + + + + : There aren't any pull requests yet. + ) + + return ( +
+ + + setPullsState(key)} + className="mb-3" + > + + + + + + router.push({ + pathname: '/repositories/:repoId/pulls', + params: {repoId: repo.id}, + query: {prefix} + })}/> + + + + + + {content} +
+ ); +}; + + +const PullsContainer = () => { + const router = useRouter() + const {repo, loading, error} = useRefs(); + const {after} = router.query; + const routerPfx = router.query.prefix || ""; + + if (loading) return ; + if (error) return ; + + return ( + { + const query = {after}; + if (router.query.prefix) { + query.prefix = router.query.prefix; + } + router.push({ + pathname: '/repositories/:repoId/pulls', + params: {repoId: repo.id}, + query + }); + }}/> + ); +}; + + +const RepositoryPullsPage = () => { + const [setActivePage] = useOutletContext(); + useEffect(() => setActivePage("pulls"), [setActivePage]); + return ; +} + +export default RepositoryPullsPage; diff --git a/webui/src/pages/repositories/repository/tags.jsx b/webui/src/pages/repositories/repository/tags.jsx index 22c9efa2375..34c93ef8316 100644 --- a/webui/src/pages/repositories/repository/tags.jsx +++ b/webui/src/pages/repositories/repository/tags.jsx @@ -40,7 +40,7 @@ const TagWidget = ({ repo, tag, onDelete }) => { return (
-
+
Date: Tue, 17 Sep 2024 16:15:58 +0300 Subject: [PATCH 2/5] Add watch build command --- Makefile | 3 +++ webui/package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/Makefile b/Makefile index a6b175dd4be..18e216456b0 100644 --- a/Makefile +++ b/Makefile @@ -342,6 +342,9 @@ $(UI_DIR)/node_modules: gen-ui: $(UI_DIR)/node_modules ## Build UI web app cd $(UI_DIR) && $(NPM) run build +gen-ui-watch: $(UI_DIR)/node_modules ## Build UI web app and watch for changes + cd $(UI_DIR) && $(NPM) run build-watch + gen-proto: ## Build Protocol Buffers (proto) files using Buf CLI go run github.com/bufbuild/buf/cmd/buf@$(BUF_CLI_VERSION) generate diff --git a/webui/package.json b/webui/package.json index 370b62cdc08..5acefbd9245 100644 --- a/webui/package.json +++ b/webui/package.json @@ -11,6 +11,7 @@ "scripts": { "dev": "vite", "build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build", + "build-watch": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build --watch", "serve": "vite preview", "test": "vitest run", "test-watch": "vitest", From 7862d150e0cb4ba334c9b08c8734c6084d85f996 Mon Sep 17 00:00:00 2001 From: Itai Gilo Date: Tue, 17 Sep 2024 17:05:21 +0300 Subject: [PATCH 3/5] Update toolbar UI --- webui/src/lib/components/controls.jsx | 4 +-- .../repositories/repository/pulls/pulls.jsx | 32 ++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/webui/src/lib/components/controls.jsx b/webui/src/lib/components/controls.jsx index 2bc4921c3a6..c584e4e2337 100644 --- a/webui/src/lib/components/controls.jsx +++ b/webui/src/lib/components/controls.jsx @@ -105,10 +105,10 @@ export const FormattedDate = ({ dateValue, format = "MM/DD/YYYY HH:mm:ss" }) => }; -export const ActionGroup = ({ children, orientation = "left" }) => { +export const ActionGroup = ({ children, orientation = "left", className = "" }) => { const side = (orientation === 'right') ? 'ms-auto' : ''; return ( -
+
{children}
); diff --git a/webui/src/pages/repositories/repository/pulls/pulls.jsx b/webui/src/pages/repositories/repository/pulls/pulls.jsx index d3441064f81..904b5f4b447 100644 --- a/webui/src/pages/repositories/repository/pulls/pulls.jsx +++ b/webui/src/pages/repositories/repository/pulls/pulls.jsx @@ -2,26 +2,19 @@ import React, {useEffect, useState} from "react"; import {useOutletContext} from "react-router-dom"; import Card from "react-bootstrap/Card"; import ListGroup from "react-bootstrap/ListGroup"; +import Alert from "react-bootstrap/Alert"; +import {Tab, Tabs} from "react-bootstrap"; +import Button from "react-bootstrap/Button"; +import TimeAgo from "react-timeago"; -import { - ActionGroup, - ActionsBar, - AlertError, - Loading, - PrefixSearchWidget, - RefreshButton -} from "../../../../lib/components/controls"; +import {ActionGroup, AlertError, Loading, PrefixSearchWidget, RefreshButton} from "../../../../lib/components/controls"; import {pulls} from "../../../../lib/api"; import {useRefs} from "../../../../lib/hooks/repo"; import {useAPIWithPagination} from "../../../../lib/hooks/api"; import {Paginator} from "../../../../lib/components/pagination"; import {useRouter} from "../../../../lib/hooks/router"; -import Alert from "react-bootstrap/Alert"; import {RepoError} from "../error"; -import {Tab, Tabs} from "react-bootstrap"; import {Link} from "../../../../lib/components/nav"; -import Button from "react-bootstrap/Button"; -import TimeAgo from "react-timeago"; const PullWidget = ({repo, pull}) => { @@ -43,7 +36,7 @@ const PullWidget = ({repo, pull}) => { params: {repoId: repo.id, pullId: pull.id}, }} > - {pull.id} + {pull.title}
Opened ago by {authorLink} @@ -59,6 +52,7 @@ const PullWidget = ({repo, pull}) => { ); }; +// TODO: is there a nicer place for this? const PullStatus = { open: "open", closed: "closed", @@ -95,19 +89,19 @@ const PullsList = ({repo, after, prefix, onPaginate}) => { return (
- - +
+
setPullsState(key)} - className="mb-3" + className="mb-3 pt-2" > - - +
+ { - +
{content}
); From 512feb187053c800c10c5ad38f9274c95625bb43 Mon Sep 17 00:00:00 2001 From: Itai Gilo Date: Tue, 17 Sep 2024 20:32:58 +0300 Subject: [PATCH 4/5] Fix PR comments --- Makefile | 3 -- webui/package-lock.json | 15 ------ webui/package.json | 1 - webui/src/lib/components/repository/tabs.jsx | 13 +++-- .../repositories/repository/pulls/pulls.jsx | 48 ++++++++----------- webui/src/styles/globals.css | 10 ---- 6 files changed, 27 insertions(+), 63 deletions(-) diff --git a/Makefile b/Makefile index 18e216456b0..a6b175dd4be 100644 --- a/Makefile +++ b/Makefile @@ -342,9 +342,6 @@ $(UI_DIR)/node_modules: gen-ui: $(UI_DIR)/node_modules ## Build UI web app cd $(UI_DIR) && $(NPM) run build -gen-ui-watch: $(UI_DIR)/node_modules ## Build UI web app and watch for changes - cd $(UI_DIR) && $(NPM) run build-watch - gen-proto: ## Build Protocol Buffers (proto) files using Buf CLI go run github.com/bufbuild/buf/cmd/buf@$(BUF_CLI_VERSION) generate diff --git a/webui/package-lock.json b/webui/package-lock.json index b350c26d5d6..7fb0a296116 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -31,7 +31,6 @@ "react-router-dom": "^6.20.1", "react-simple-code-editor": "^0.13.1", "react-syntax-highlighter": "^15.5.0", - "react-timeago": "^7.2.0", "react-toggle-dark-mode": "^1.1.1", "rehype-raw": "^7.0.0", "rehype-react": "^8.0.0", @@ -20215,14 +20214,6 @@ "node": "*" } }, - "node_modules/react-timeago": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/react-timeago/-/react-timeago-7.2.0.tgz", - "integrity": "sha512-2KsBEEs+qRhKx/kekUVNSTIpop3Jwd7SRBm0R4Eiq3mPeswRGSsftY9FpKsE/lXLdURyQFiHeHFrIUxLYskG5g==", - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-toggle-dark-mode": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/react-toggle-dark-mode/-/react-toggle-dark-mode-1.1.1.tgz", @@ -38278,12 +38269,6 @@ } } }, - "react-timeago": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/react-timeago/-/react-timeago-7.2.0.tgz", - "integrity": "sha512-2KsBEEs+qRhKx/kekUVNSTIpop3Jwd7SRBm0R4Eiq3mPeswRGSsftY9FpKsE/lXLdURyQFiHeHFrIUxLYskG5g==", - "requires": {} - }, "react-toggle-dark-mode": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/react-toggle-dark-mode/-/react-toggle-dark-mode-1.1.1.tgz", diff --git a/webui/package.json b/webui/package.json index 5acefbd9245..f102d64e216 100644 --- a/webui/package.json +++ b/webui/package.json @@ -42,7 +42,6 @@ "react-router-dom": "^6.20.1", "react-simple-code-editor": "^0.13.1", "react-syntax-highlighter": "^15.5.0", - "react-timeago": "^7.2.0", "react-toggle-dark-mode": "^1.1.1", "rehype-raw": "^7.0.0", "rehype-react": "^8.0.0", diff --git a/webui/src/lib/components/repository/tabs.jsx b/webui/src/lib/components/repository/tabs.jsx index 995481b9f3d..431082301f7 100644 --- a/webui/src/lib/components/repository/tabs.jsx +++ b/webui/src/lib/components/repository/tabs.jsx @@ -1,15 +1,15 @@ import React from "react"; import Nav from "react-bootstrap/Nav"; -import {FileDiffIcon, GitCommitIcon, DatabaseIcon, GitBranchIcon, GitCompareIcon, PlayIcon, GearIcon, TagIcon} from "@primer/octicons-react"; +import {FileDiffIcon, GitCommitIcon, DatabaseIcon, GitBranchIcon, GitPullRequestIcon, GitCompareIcon, PlayIcon, GearIcon, TagIcon} from "@primer/octicons-react"; import {useRefs} from "../../hooks/repo"; import {Link, NavItem} from "../nav"; import {useRouter} from "../../hooks/router"; import {RefTypeBranch} from "../../../constants"; -// TODO: this is temp, until PRfD will be ready -const showPulls = false; +// TODO (gilo): this is temp, until PRfD will be ready +const showPulls = true; export const RepositoryNavTabs = ({ active }) => { const { reference } = useRefs(); @@ -60,9 +60,12 @@ export const RepositoryNavTabs = ({ active }) => { Tags - {showPulls && + { + // TODO (gilo): this is temp, until PRfD will be ready + showPulls && - Pull Requests + {/* TODO (gilo): the icon is very similar to the compare icon, consider changing it*/} + Pull Requests } diff --git a/webui/src/pages/repositories/repository/pulls/pulls.jsx b/webui/src/pages/repositories/repository/pulls/pulls.jsx index 904b5f4b447..6d29c33922a 100644 --- a/webui/src/pages/repositories/repository/pulls/pulls.jsx +++ b/webui/src/pages/repositories/repository/pulls/pulls.jsx @@ -5,7 +5,7 @@ import ListGroup from "react-bootstrap/ListGroup"; import Alert from "react-bootstrap/Alert"; import {Tab, Tabs} from "react-bootstrap"; import Button from "react-bootstrap/Button"; -import TimeAgo from "react-timeago"; +import dayjs from "dayjs"; import {ActionGroup, AlertError, Loading, PrefixSearchWidget, RefreshButton} from "../../../../lib/components/controls"; import {pulls} from "../../../../lib/api"; @@ -18,41 +18,31 @@ import {Link} from "../../../../lib/components/nav"; const PullWidget = ({repo, pull}) => { - const authorLink = - - {pull.author} - ; return ( - -
-
- - {pull.title} + +
+
+ + {pull.title} -
- Opened ago by {authorLink} -
-
-
- - - -
+
+ + Opened {dayjs.unix(pull.created_at).fromNow()} by {pull.author} + +
+
+ + +
); }; -// TODO: is there a nicer place for this? +// TODO (gilo): is there a nicer place for this? const PullStatus = { open: "open", closed: "closed", diff --git a/webui/src/styles/globals.css b/webui/src/styles/globals.css index cbf151b09cc..104cfd15d43 100644 --- a/webui/src/styles/globals.css +++ b/webui/src/styles/globals.css @@ -919,13 +919,3 @@ td.entry-type-indicator { .upload-item-done { color: var(--bs-success); } - -.pull-row .pull-title { - text-decoration: none; - color: var(--bs-body-color); -} - -.pull-row .pull-info { - text-decoration: none; - color: var(--bs-secondary); -} From 033a2a386e6e8cd78230de5decea3414310a85b7 Mon Sep 17 00:00:00 2001 From: Itai Gilo Date: Wed, 18 Sep 2024 08:42:47 +0300 Subject: [PATCH 5/5] Hide feature --- webui/src/lib/components/repository/tabs.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webui/src/lib/components/repository/tabs.jsx b/webui/src/lib/components/repository/tabs.jsx index 431082301f7..f050023702f 100644 --- a/webui/src/lib/components/repository/tabs.jsx +++ b/webui/src/lib/components/repository/tabs.jsx @@ -9,7 +9,7 @@ import {useRouter} from "../../hooks/router"; import {RefTypeBranch} from "../../../constants"; // TODO (gilo): this is temp, until PRfD will be ready -const showPulls = true; +const showPulls = false; export const RepositoryNavTabs = ({ active }) => { const { reference } = useRefs();