import type { ReactView } from "@ethicdevs/react-monolith";
import React from "react";
import {
PullRequestState,
type Organization,
type PullRequest,
type User,
} from "@prisma/client";
import type {
CommonProps,
RepositoryFileDiff,
RepositoryObject,
RepositoryWithForkedFromRepo,
} from "../../types";
import { AppRoute } from "../../routes.defs";
import { buildRouteLink } from "../../utils/shared";
import {
Card,
Grid,
InlineCode,
IslandWrapper,
Layout,
MarkdownToJsx,
PageWrapper,
TextInput,
TextArea,
} from "../../components";
import RepositoryFilesDiffsList from "../../islands/RepositoryFilesDiffsList";
import RepositoryHero from "../../islands/RepositoryHero";
export interface RepositoryPullRequestDetailsViewProps extends CommonProps {
filesDiffs: RepositoryFileDiff[];
lastCommit: RepositoryObject | null;
pullRequest: PullRequest;
pullRequestAuthor: User;
sourceParentOrg: Organization;
sourceRepo: RepositoryWithForkedFromRepo;
targetParentOrg: Organization;
targetRepo: RepositoryWithForkedFromRepo;
isCurrentUserAllowedToMerge?: boolean;
}
const RepositoryPullRequestDetailsView: ReactView<
RepositoryPullRequestDetailsViewProps
> = ({
commonProps,
filesDiffs,
lastCommit,
pullRequest: pr,
pullRequestAuthor: prAuthor,
sourceParentOrg,
sourceRepo,
targetParentOrg: parentOrg,
targetRepo: repo,
isCurrentUserAllowedToMerge = false,
}) => {
const totalDiff = filesDiffs.reduce(
(acc, diff) => {
acc = {
...acc,
additions: (acc?.additions || 0) + diff.additions,
deletions: (acc?.deletions || 0) + diff.deletions,
};
return acc;
},
{ additions: 0, deletions: 0 },
);
if (pr.state !== PullRequestState.OPEN) {
isCurrentUserAllowedToMerge = false;
}
return (
<Layout
{...commonProps}
showDrawerPrimary
orgSlug={parentOrg.slug}
repoSlug={repo.slug}
>
<PageWrapper>
<IslandWrapper data-islandid={`${RepositoryHero.name}$$0`}>
<RepositoryHero
forkedFromRepo={repo.forkedFromRepo}
forksCount={repo.forks.length}
parentOrg={parentOrg}
path={`Pull Request / #${pr.uid}`}
repo={repo}
/>
</IslandWrapper>
<Grid.Col fluid style={{ marginTop: 24 }}>
<Grid.Col key={pr.id} fluid>
<Grid.Row fluid alignItems="center" gap={16}>
<h1 style={{ margin: 0, marginTop: 8 }}>{pr.summary}</h1>
<span>
<InlineCode themeScheme={commonProps.themeScheme}>
{`[${pr.state}]`}
</InlineCode>
</span>
</Grid.Row>
<Grid.Row
fluid
nowrap
gap={16}
style={{ opacity: 0.67, marginTop: 8 }}
>
<a
href={buildRouteLink(
AppRoute.USER_DETAILS,
{
username: prAuthor.username,
},
{ encodeURIComponent: false },
)}
>
{prAuthor.displayName || prAuthor.username}
</a>
{isCurrentUserAllowedToMerge && (
<>
<a
href={buildRouteLink(
AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION,
{
orgSlug: parentOrg.slug,
repoSlug: repo.slug,
pullUid: pr.uid,
},
)}
>
Edit PR
</a>
<a
href={buildRouteLink(
AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION,
{
orgSlug: parentOrg.slug,
repoSlug: repo.slug,
pullUid: pr.uid,
},
)}
>
Delete PR
</a>
</>
)}
</Grid.Row>
<span style={{ opacity: 0.67, marginTop: 8 }}>
{"wants to merge branch "}
<InlineCode themeScheme={commonProps.themeScheme}>
{pr.sourceBranch}
</InlineCode>
{" from repository "}
<InlineCode themeScheme={commonProps.themeScheme}>
{`${sourceParentOrg.slug}/${sourceRepo.slug}`}
</InlineCode>
{" (source) "}
</span>
<span style={{ opacity: 0.67, marginTop: 4 }}>
{" into branch "}
<InlineCode themeScheme={commonProps.themeScheme}>
{pr.targetBranch}
</InlineCode>
{" of repository "}
<InlineCode themeScheme={commonProps.themeScheme}>
{`${parentOrg.slug}/${repo.slug}`}
</InlineCode>
{" (target) "}
</span>
<Grid.Row
fluid
alignItems={"center"}
style={{ opacity: 0.67, marginTop: 8 }}
>
{new Date(pr.createdAt).getTime() <=
new Date(pr.updatedAt).getTime() && (
<span>opened on {new Date(pr.createdAt).toLocaleString()}</span>
)}
{((pr.closedAt == null &&
new Date(pr.updatedAt).getTime() >
new Date(pr.createdAt).getTime()) ||
(pr.closedAt != null &&
new Date(pr.updatedAt).getTime() <
new Date(pr.closedAt).getTime())) && (
<span>
updated on {new Date(pr.updatedAt).toLocaleString()}
</span>
)}
{pr.closedAt != null && (
<span>
closed on
{new Date(pr.closedAt).toLocaleString()}
</span>
)}
</Grid.Row>
</Grid.Col>
{isCurrentUserAllowedToMerge && (
<Card
style={{ width: "100%", padding: 8, marginTop: 16 }}
themeScheme={commonProps.themeScheme}
>
<Grid.Col fluid style={{ marginTop: 8 }}>
<form
style={{ width: "100%" }}
method={"POST"}
action={buildRouteLink(
AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION,
{
orgSlug: parentOrg.slug,
repoSlug: repo.slug,
pullUid: pr.uid,
},
)}
>
<Grid.Col fluid nowrap gap={8}>
<TextInput
themeScheme={commonProps.themeScheme}
name={"merge_summary"}
type={"text"}
placeholder={
"Enter a short description of the merged code..."
}
style={{ width: "100%" }}
/>
<TextArea
themeScheme={commonProps.themeScheme}
name={"merge_message"}
placeholder={
"Describe what this merge will bring when merged in target repository..."
}
style={{ width: "100%", minHeight: 180 }}
></TextArea>
<Grid.Row
fluid
nowrap
justifyContent={"flex-end"}
gap={8}
alignItems={"center"}
style={{ marginTop: 8 }}
>
<button type={"submit"} name={"merge_default"}>
Merge
</button>
<button type={"submit"} name={"merge_squash"}>
Merge w/ Squash
</button>
<button type={"submit"} name={"merge_rebase"}>
Merge w/ Rebase
</button>
</Grid.Row>
</Grid.Col>
</form>
</Grid.Col>
</Card>
)}
<Card
style={{ width: "100%", padding: 8, marginTop: 8 }}
themeScheme={commonProps.themeScheme}
>
<Grid.Row
fluid
nowrap
alignItems={"center"}
style={{ marginBottom: 8 }}
gap={16}
>
<div style={{ color: "rgb(43, 176, 90)" }}>
<strong>+</strong> <span>{totalDiff.additions}</span>
</div>
<div style={{ color: "rgb(215, 44, 44)" }}>
<strong>-</strong> <span>{totalDiff.deletions}</span>
</div>
</Grid.Row>
<MarkdownToJsx
themeScheme={commonProps.themeScheme}
markdown={
pr.textMd == null || pr.textMd.trim() === ""
? "> <no_description_yet />"
: pr.textMd
}
/>
</Card>
<Grid.Col
gap={8}
fluid
data-islandid={`${RepositoryFilesDiffsList.name}$$0`}
style={{ marginTop: 8, padding: 0 }}
>
<RepositoryFilesDiffsList
filesDiffs={filesDiffs}
themeScheme={commonProps.themeScheme}
orgSlug={parentOrg.slug}
repoSlug={repo.slug}
commitHash={lastCommit != null ? lastCommit.commit : "HEAD"}
/>
</Grid.Col>
</Grid.Col>
</PageWrapper>
</Layout>
);
};
RepositoryPullRequestDetailsView.displayName =
"RepositoryPullRequestDetailsView";
export default RepositoryPullRequestDetailsView;