Skip to content

Commit

Permalink
PRfD - List Pull Requests UI (#8171)
Browse files Browse the repository at this point in the history
* Add UI for List Pull Requests

* Add watch build command

* Update toolbar UI

* Fix PR comments

* Hide feature
  • Loading branch information
itaigilo authored Sep 18, 2024
1 parent 066d8f5 commit feee276
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 12 deletions.
1 change: 1 addition & 0 deletions webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
59 changes: 58 additions & 1 deletion webui/src/lib/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions webui/src/lib/components/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div role="toolbar" className={`${side} mb-2 btn-toolbar action-group-${orientation}`}>
<div role="toolbar" className={`${side} mb-2 btn-toolbar action-group-${orientation} ${className}`}>
{children}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion webui/src/lib/components/repository/changes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export const ChangesTreeContainer = ({results, delimiter, uriNavigator,
<div>{changesTreeMessage}</div>
<Card>
<Card.Header>
<span className="float-start">
<span className="float-start w-100">
{(delimiter !== "") && uriNavigator}
</span>
</Card.Header>
Expand Down
13 changes: 11 additions & 2 deletions webui/src/lib/components/repository/tabs.jsx
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -59,6 +60,14 @@ export const RepositoryNavTabs = ({ active }) => {
<Link active={active === 'tags'} href={`/repositories/${repoId}/tags`} component={NavItem}>
<TagIcon/> Tags
</Link>
{
// TODO (gilo): this is temp, until PRfD will be ready
showPulls &&
<Link active={active === 'pulls'} href={`/repositories/${repoId}/pulls`} component={NavItem}>
{/* TODO (gilo): the icon is very similar to the compare icon, consider changing it*/}
<GitPullRequestIcon/> Pull Requests
</Link>
}
<Link active={active === 'compare'} href={withRefAndCompareContext(`/repositories/${repoId}/compare`)} component={NavItem}>
<GitCompareIcon/> Compare
</Link>
Expand Down
2 changes: 2 additions & 0 deletions webui/src/pages/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -62,6 +63,7 @@ export const IndexPage = () => {
</Route>
<Route path="branches" element={<RepositoryBranchesPage/>}/>
<Route path="tags" element={<RepositoryTagsPage/>}/>
<Route path="pulls" element={<RepositoryPullsPage/>}/>
<Route path="compare/*" element={<RepositoryComparePage/>}/>
<Route path="actions">
<Route index element={<RepositoryActionsPage/>}/>
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/repositories/repository/commits/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const CommitWidget = ({ repo, commit }) => {
return (
<ListGroup.Item>
<div className="clearfix">
<div className="float-start">
<div className="float-start w-100">
<h6>
<Link href={{
pathname: '/repositories/:repoId/commits/:commitId',
Expand Down
149 changes: 149 additions & 0 deletions webui/src/pages/repositories/repository/pulls/pulls.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
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 dayjs from "dayjs";

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 {RepoError} from "../error";
import {Link} from "../../../../lib/components/nav";


const PullWidget = ({repo, pull}) => {
return (
<ListGroup.Item className="pull-row pt-3 pb-3 clearfix">
<div className="float-start">
<h6>
<Link href={{
pathname: '/repositories/:repoId/pulls/:pullId',
params: {repoId: repo.id, pullId: pull.id}
}}>
<span>{pull.title}</span>
</Link>
</h6>
<small>
Opened {dayjs.unix(pull.created_at).fromNow()} by <strong>{pull.author}</strong>
</small>
</div>
<div className="float-end mt-2">
<Button variant="secondary" size="sm" disabled={true}>{pull.source_branch}</Button>
<span className="m-2">&#8680;</span>
<Button variant="secondary" size="sm" disabled={true}>{pull.destination_branch}</Button>
</div>
</ListGroup.Item>
);
};

// 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 = <Loading/>;
else if (error) content = <AlertError error={error}/>;
else content = (results && !!results.length ?
<>
<Card>
<ListGroup variant="flush">
{results.map(pull => (
<PullWidget key={pull.id} repo={repo} pull={pull}/>
))}
</ListGroup>
</Card>
<Paginator onPaginate={onPaginate} nextPage={nextPage} after={after}/>
</> : <Alert variant="info">There aren&apos;t any pull requests yet.</Alert>
)

return (
<div className="mb-5">
<div className="position-relative clearfix">
<div className="">
<Tabs
defaultActiveKey={pullsState}
id="pulls-tabs"
onSelect={key => setPullsState(key)}
className="mb-3 pt-2"
>
<Tab eventKey={PullStatus.open} title="Open"/>
<Tab eventKey={PullStatus.closed} title="Closed"/>
</Tabs>
</div>
<ActionGroup orientation="right" className="position-absolute top-0 end-0 pb-2">
<PrefixSearchWidget
defaultValue={router.query.prefix}
text="Find Pull Request"
onFilter={prefix => router.push({
pathname: '/repositories/:repoId/pulls',
params: {repoId: repo.id},
query: {prefix}
})}/>

<RefreshButton onClick={doRefresh}/>
<Button variant="success">Create Pull Request</Button>
</ActionGroup>
</div>
{content}
</div>
);
};


const PullsContainer = () => {
const router = useRouter()
const {repo, loading, error} = useRefs();
const {after} = router.query;
const routerPfx = router.query.prefix || "";

if (loading) return <Loading/>;
if (error) return <RepoError error={error}/>;

return (
<PullsList
repo={repo}
after={after || ""}
prefix={routerPfx}
onPaginate={after => {
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 <PullsContainer/>;
}

export default RepositoryPullsPage;
2 changes: 1 addition & 1 deletion webui/src/pages/repositories/repository/tags.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const TagWidget = ({ repo, tag, onDelete }) => {
return (
<ListGroup.Item>
<div className="clearfix">
<div className="float-start">
<div className="float-start w-100">
<h6>
<Link href={{
pathname: '/repositories/:repoId/objects',
Expand Down
4 changes: 0 additions & 4 deletions webui/src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,6 @@
border-bottom: none;
}

.float-start {
width: 100%;
}

.action-bar .float-start .btn {
margin-right: 10px;
}
Expand Down

0 comments on commit feee276

Please sign in to comment.