From feee27698a5ae2d18322b6f6776b29372d785b5c Mon Sep 17 00:00:00 2001 From: itaigilo Date: Wed, 18 Sep 2024 09:03:59 +0300 Subject: [PATCH] PRfD - List Pull Requests UI (#8171) * Add UI for List Pull Requests * Add watch build command * Update toolbar UI * Fix PR comments * Hide feature --- webui/package.json | 1 + webui/src/lib/api/index.js | 59 ++++++- webui/src/lib/components/controls.jsx | 4 +- .../src/lib/components/repository/changes.jsx | 2 +- webui/src/lib/components/repository/tabs.jsx | 13 +- webui/src/pages/index.jsx | 2 + .../repositories/repository/commits/index.jsx | 2 +- .../repositories/repository/pulls/pulls.jsx | 149 ++++++++++++++++++ .../pages/repositories/repository/tags.jsx | 2 +- webui/src/styles/globals.css | 4 - 10 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 webui/src/pages/repositories/repository/pulls/pulls.jsx diff --git a/webui/package.json b/webui/package.json index 84a197d631e..f102d64e216 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", 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/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/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..f050023702f 100644 --- a/webui/src/lib/components/repository/tabs.jsx +++ b/webui/src/lib/components/repository/tabs.jsx @@ -1,14 +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 (gilo): this is temp, until PRfD will be ready +const showPulls = false; export const RepositoryNavTabs = ({ active }) => { const { reference } = useRefs(); @@ -59,6 +60,14 @@ export const RepositoryNavTabs = ({ active }) => { Tags + { + // TODO (gilo): this is temp, until PRfD will be ready + showPulls && + + {/* TODO (gilo): the icon is very similar to the compare icon, consider changing it*/} + 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 (
-
+
{ + return ( + +
+
+ + {pull.title} + +
+ + Opened {dayjs.unix(pull.created_at).fromNow()} by {pull.author} + +
+
+ + + +
+
+ ); +}; + +// TODO (gilo): is there a nicer place for this? +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 pt-2" + > + + + +
+ + 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 (
-
+