feat(pull_request): started to implement the new pr flow [wip]@@ -1,5 +1,5 @@
{
- "_generatedAtUnix": 1665017412010,
+ "_generatedAtUnix": 1665025556931,
"_hashAlgorithm": "sha1",
"_version": 2,
"islands": {
@@ -57,6 +57,12 @@
"pathBundle": "./public/.islands/RepositoryInitialSetup.bundle.js",
"pathSourceMap": "./public/.islands/RepositoryInitialSetup.bundle.js.map"
},
+ "RepositoryPullRequestCreateForm": {
+ "hash": "2b062b98a7da02483b72dd96f7093dfee181c638",
+ "pathSource": "./app/islands/RepositoryPullRequestCreateForm.tsx",
+ "pathBundle": "./public/.islands/RepositoryPullRequestCreateForm.bundle.js",
+ "pathSourceMap": "./public/.islands/RepositoryPullRequestCreateForm.bundle.js.map"
+ },
"RepositoryTreeView": {
"hash": "846a1f7c4654c973bd106763dee544eb2a485ab2",
"pathSource": "./app/islands/RepositoryTreeView.tsx",
@@ -114,7 +120,7 @@
"pathSource": "./app/views/repository/RepositoryForkView.tsx"
},
"RepositoryPullRequestCreateView": {
- "hash": "4fac956fc1de8340ad13b037dab93ad2ef2eca89",
+ "hash": "6ebba879724136e446776d131ab071232b838774",
"pathSource": "./app/views/repository/RepositoryPullRequestCreateView.tsx"
},
"RepositoryPullRequestsView": {
@@ -5,6 +5,11 @@ import { AppRoute, AppRoutesParams } from "../../routes";
// app services
import { makeOrganizationService } from "../../services/organization";
import { makeRepositoryService } from "../../services/repository";
+// app islands
+import {
+ PullRequestFormState,
+ RepositoryPullRequestCreateFormVariant,
+} from "../../islands/RepositoryPullRequestCreateForm";
// app views
import RepositoryPullRequestCreateView, {
RepositoryPullRequestCreateViewProps,
@@ -27,6 +32,18 @@ const getRepositoryPullRequestCreateView: ReqHandler = async (
return reply.status(404).callNotFound();
}
+ const variant: RepositoryPullRequestCreateFormVariant = {
+ state: PullRequestFormState.CONFIGURE,
+ data: {
+ source: {
+ parentOrg,
+ repo,
+ isFork: repo.forkedFromRepoId != null,
+ },
+ initialTarget: undefined,
+ },
+ };
+
const reqHandler = reply.makeRequestHandler(request, reply);
return reqHandler<RepositoryPullRequestCreateViewProps>(
RepositoryPullRequestCreateView.name,
@@ -35,6 +52,7 @@ const getRepositoryPullRequestCreateView: ReqHandler = async (
initialValues: {},
parentOrg,
repo,
+ variant,
}
);
};
@@ -2,6 +2,11 @@
import { ReqHandler } from "@ethicdevs/react-monolith";
// app
import { AppRoute, AppRoutesParams } from "../../routes";
+// app islands
+import {
+ PullRequestFormState,
+ RepositoryPullRequestCreateFormVariant,
+} from "../../islands/RepositoryPullRequestCreateForm";
// app services
import { makeOrganizationService } from "../../services/organization";
import { makeRepositoryService } from "../../services/repository";
@@ -15,7 +20,9 @@ const postRepositoryPullRequestCreateAction: ReqHandler = async (
reply
) => {
const { orgSlug, repoSlug } =
- request.params as AppRoutesParams[AppRoute.REPOSITORY_PULL_REQUEST_CREATE]["params"];
+ request.params as AppRoutesParams[AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]["params"];
+ const { state_from: fromState, state_dest: desiredState } =
+ request.body as AppRoutesParams[AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]["body"];
const orgService = makeOrganizationService({ request });
const repoService = makeRepositoryService({ request });
@@ -27,6 +34,55 @@ const postRepositoryPullRequestCreateAction: ReqHandler = async (
return reply.status(404).callNotFound();
}
+ if (
+ fromState === PullRequestFormState.ERROR ||
+ desiredState === PullRequestFormState.CONFIGURE
+ ) {
+ let redirectUri =
+ request.namedViewsPathMap[AppRoute.REPOSITORY_PULL_REQUEST_CREATE];
+ redirectUri = redirectUri
+ .replace(/:orgSlug/g, parentOrg.slug)
+ .replace(/:repoSlug/g, repo.slug);
+ reply.redirect(302, redirectUri);
+ return reply;
+ }
+
+ let variant: RepositoryPullRequestCreateFormVariant | null = null;
+
+ if (desiredState === PullRequestFormState.COMPARE) {
+ variant = {
+ state: desiredState,
+ data: {
+ source: {
+ parentOrg,
+ repo,
+ branch: "",
+ },
+ target: {
+ parentOrg,
+ repo,
+ branch: "",
+ },
+ },
+ };
+ } else if (desiredState === PullRequestFormState.DETAILS) {
+ variant = {
+ state: desiredState,
+ data: {
+ canCurrentUserSubmitPullRequest: false,
+ },
+ };
+ }
+
+ if (variant == null) {
+ variant = {
+ state: PullRequestFormState.ERROR,
+ data: {
+ errorMessage: "Something went wrong",
+ },
+ };
+ }
+
const reqHandler = reply.makeRequestHandler(request, reply);
return reqHandler<RepositoryPullRequestCreateViewProps>(
RepositoryPullRequestCreateView.name,
@@ -35,6 +91,7 @@ const postRepositoryPullRequestCreateAction: ReqHandler = async (
initialValues: {},
parentOrg,
repo,
+ variant,
}
);
};
@@ -0,0 +1,203 @@
+// 1st-party
+import type { ReactIsland } from "@ethicdevs/react-monolith";
+// 3rd-party
+import React from "react";
+// generated via script[generate:prisma]
+import type { Organization, Repository } from "@prisma/client";
+// app
+import type { RepositoryForkedFromRepoMeta } from "../types";
+import { Grid } from "../components/Grid";
+
+export enum PullRequestFormState {
+ CONFIGURE = "configure",
+ COMPARE = "compare",
+ DETAILS = "input_details",
+ ERROR = "error",
+}
+
+export interface RepositoryPullRequestCreateFormPropsCommon {
+ errorMessage?: string | null;
+}
+
+export type RepositoryPullRequestCreateFormPropsByState<
+ S extends PullRequestFormState = PullRequestFormState
+> = S extends PullRequestFormState.CONFIGURE
+ ? {
+ source: {
+ parentOrg: Organization;
+ repo: Repository;
+ isFork: boolean;
+ forkedFromRepoMetas?: RepositoryForkedFromRepoMeta;
+ };
+ initialTarget?: {
+ parentOrg: Organization;
+ repo: Repository;
+ };
+ }
+ : S extends PullRequestFormState.COMPARE
+ ? {
+ source: {
+ parentOrg: Organization;
+ repo: Repository;
+ branch: string;
+ };
+ target: {
+ parentOrg: Organization;
+ repo: Repository;
+ branch: string;
+ };
+ }
+ : S extends PullRequestFormState.DETAILS
+ ? {
+ canCurrentUserSubmitPullRequest: boolean;
+ }
+ : S extends PullRequestFormState.ERROR
+ ? {
+ errorMessage: string;
+ }
+ : never;
+
+export type RepositoryPullRequestCreateFormVariant =
+ | {
+ state: PullRequestFormState.CONFIGURE;
+ data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.CONFIGURE>;
+ }
+ | {
+ state: PullRequestFormState.COMPARE;
+ data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.COMPARE>;
+ }
+ | {
+ state: PullRequestFormState.DETAILS;
+ data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.DETAILS>;
+ }
+ | {
+ state: PullRequestFormState.ERROR;
+ data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.ERROR>;
+ };
+
+export interface RepositoryPullRequestCreateFormProps
+ extends RepositoryPullRequestCreateFormPropsCommon {
+ variant: RepositoryPullRequestCreateFormVariant;
+}
+
+// PullRequestFormState.CONFIGURE
+const isConfigureState = (
+ s: PullRequestFormState
+): s is PullRequestFormState.CONFIGURE =>
+ typeof s !== "undefined" && s != null && s === PullRequestFormState.CONFIGURE;
+const isConfigureStateData = (
+ s: PullRequestFormState,
+ i: unknown
+): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.CONFIGURE> =>
+ typeof i !== "undefined" && i != null && s === PullRequestFormState.CONFIGURE;
+// PullRequestFormState.COMPARE
+const isCompareState = (
+ s: PullRequestFormState
+): s is PullRequestFormState.COMPARE =>
+ typeof s !== "undefined" && s != null && s === PullRequestFormState.COMPARE;
+const isCompareStateData = (
+ s: PullRequestFormState,
+ i: unknown
+): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.COMPARE> =>
+ typeof i !== "undefined" && i != null && s === PullRequestFormState.COMPARE;
+// PullRequestFormState.DETAILS
+const isDetailsState = (
+ s: PullRequestFormState
+): s is PullRequestFormState.DETAILS =>
+ typeof s !== "undefined" && s != null && s === PullRequestFormState.DETAILS;
+const isDetailsStateData = (
+ s: PullRequestFormState,
+ i: unknown
+): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.DETAILS> =>
+ typeof i !== "undefined" && i != null && s === PullRequestFormState.DETAILS;
+// PullRequestFormState.ERROR
+const isErrorState = (
+ s: PullRequestFormState
+): s is PullRequestFormState.ERROR =>
+ typeof s !== "undefined" && s != null && s === PullRequestFormState.ERROR;
+const isErrorStateData = (
+ s: PullRequestFormState,
+ i: unknown
+): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.ERROR> =>
+ typeof i !== "undefined" && i != null && s === PullRequestFormState.ERROR;
+
+const RepositoryPullRequestCreateForm: ReactIsland<RepositoryPullRequestCreateFormProps> =
+ ({ variant: { state, data } }) => {
+ // PullRequestFormState.CONFIGURE
+ if (isConfigureState(state) && isConfigureStateData(state, data)) {
+ return (
+ <Grid.Col fluid nowrap>
+ <h1>Configure input and output (org, repo, branch)</h1>
+ <form
+ method={"POST"}
+ action={`/${data.source.parentOrg.slug}/${data.source.repo.slug}/pulls/new`}
+ >
+ <input type={"hidden"} name={"state_from"} value={state} />
+ <input
+ type={"hidden"}
+ name={"state_dest"}
+ value={PullRequestFormState.COMPARE}
+ />
+ <button type={"submit"}>Go to compare</button>
+ </form>
+ <div>
+ <pre>
+ <code>{JSON.stringify(data, null, 2)}</code>
+ </pre>
+ </div>
+ </Grid.Col>
+ );
+ }
+ // PullRequestFormState.COMPARE
+ if (isCompareState(state) && isCompareStateData(state, data)) {
+ return (
+ <Grid.Col fluid nowrap>
+ <h1>Compare input and output (org, repo, branch)</h1>
+ <form
+ method={"POST"}
+ action={`/${data.source.parentOrg.slug}/${data.source.repo.slug}/pulls/new`}
+ >
+ <input type={"hidden"} name={"state_from"} value={state} />
+ <input
+ type={"hidden"}
+ name={"state_dest"}
+ value={PullRequestFormState.DETAILS}
+ />
+ <button type={"submit"}>Go to add details</button>
+ </form>
+ <div>
+ <pre>
+ <code>{JSON.stringify(data, null, 2)}</code>
+ </pre>
+ </div>
+ </Grid.Col>
+ );
+ }
+ // PullRequestFormState.DETAILS
+ if (isDetailsState(state) && isDetailsStateData(state, data)) {
+ return (
+ <Grid.Col fluid nowrap>
+ <h1>Add details about the Pull Request:</h1>
+ <div>
+ <pre>
+ <code>{JSON.stringify(data, null, 2)}</code>
+ </pre>
+ </div>
+ </Grid.Col>
+ );
+ }
+ // PullRequestFormState.ERROR
+ if (isErrorState(state) && isErrorStateData(state, data)) {
+ const { errorMessage } = data;
+ return (
+ <Grid.Col fluid nowrap>
+ <h1>Woops, an error occurred:</h1>
+ <div>{errorMessage}</div>
+ </Grid.Col>
+ );
+ }
+ return null;
+ };
+
+RepositoryPullRequestCreateForm.displayName = "RepositoryPullRequestCreateForm";
+export default RepositoryPullRequestCreateForm;
@@ -9,6 +9,8 @@ import { ResourceVisibility } from "@prisma/client";
// app
import type { AppThemeScheme } from "./types";
import { authenticatedOrLogin, guestOrRedirect } from "./utils/server";
+// app islands
+import type { PullRequestFormState } from "./islands/RepositoryPullRequestCreateForm";
// app controllers
import {
AuthController,
@@ -157,6 +159,8 @@ export interface AppRoutesParams extends IRouteParams {
repoSlug: string;
};
body: {
+ state_from: PullRequestFormState;
+ state_dest: PullRequestFormState;
summary: string;
description: string;
source_parent_org_slug: string;
@@ -486,6 +490,9 @@ export const AppRoutesSchemas: Record<AppRoute, undefined | FastifySchema> = {
body: {
type: "object",
required: [
+ "state_from",
+ "state_dest",
+ // --
"summary",
"source_parent_org_slug",
"source_repository_slug",
@@ -496,6 +503,12 @@ export const AppRoutesSchemas: Record<AppRoute, undefined | FastifySchema> = {
],
additionalProperties: false,
properties: {
+ state_from: {
+ type: "string",
+ },
+ state_dest: {
+ type: "string",
+ },
summary: {
type: "string",
},
@@ -6,19 +6,23 @@ import React from "react";
import type { Organization, PullRequest, Repository } from "@prisma/client";
// app
import type { CommonProps } from "../../types";
-import { Grid, IslandWrapper, Layout, PageWrapper } from "../../components";
+import { IslandWrapper, Layout, PageWrapper } from "../../components";
// app islands
import RepositoryHero from "../../islands/RepositoryHero";
+import RepositoryPullRequestCreateForm, {
+ RepositoryPullRequestCreateFormVariant,
+} from "../../islands/RepositoryPullRequestCreateForm";
export interface RepositoryPullRequestCreateViewProps extends CommonProps {
errorMessage?: string | null;
initialValues?: PullRequest;
parentOrg: Organization;
repo: Repository;
+ variant: RepositoryPullRequestCreateFormVariant;
}
const RepositoryPullRequestCreateView: ReactView<RepositoryPullRequestCreateViewProps> =
- ({ commonProps, parentOrg, repo }) => {
+ ({ commonProps, parentOrg, repo, variant }) => {
return (
<Layout {...commonProps}>
<PageWrapper>
@@ -29,7 +33,11 @@ const RepositoryPullRequestCreateView: ReactView<RepositoryPullRequestCreateView
repo={repo}
/>
</IslandWrapper>
- <Grid.Col fluid style={{ marginTop: 32 }}></Grid.Col>
+ <IslandWrapper
+ data-islandid={`${RepositoryPullRequestCreateForm.name}$$0`}
+ >
+ <RepositoryPullRequestCreateForm variant={variant} />
+ </IslandWrapper>
</PageWrapper>
</Layout>
);
@@ -9,6 +9,8 @@ import { ResourceVisibility } from "@prisma/client";
// app
import type { AppThemeScheme } from "./types";
import { authenticatedOrLogin, guestOrRedirect } from "./utils/server";
+// app islands
+import type { PullRequestFormState } from "./islands/RepositoryPullRequestCreateForm";
// app controllers
import {
AuthController,
@@ -157,6 +159,8 @@ export interface AppRoutesParams extends IRouteParams {
repoSlug: string;
};
body: {
+ state_from: PullRequestFormState;
+ state_dest: PullRequestFormState;
summary: string;
description: string;
source_parent_org_slug: string;
@@ -486,6 +490,9 @@ export const AppRoutesSchemas: Record<AppRoute, undefined | FastifySchema> = {
body: {
type: "object",
required: [
+ "state_from",
+ "state_dest",
+ // --
"summary",
"source_parent_org_slug",
"source_repository_slug",
@@ -496,6 +503,12 @@ export const AppRoutesSchemas: Record<AppRoute, undefined | FastifySchema> = {
],
additionalProperties: false,
properties: {
+ state_from: {
+ type: "string",
+ },
+ state_dest: {
+ type: "string",
+ },
summary: {
type: "string",
},
@@ -6,19 +6,23 @@ import React from "react";
import type { Organization, PullRequest, Repository } from "@prisma/client";
// app
import type { CommonProps } from "../../types";
-import { Grid, IslandWrapper, Layout, PageWrapper } from "../../components";
+import { IslandWrapper, Layout, PageWrapper } from "../../components";
// app islands
import RepositoryHero from "../../islands/RepositoryHero";
+import RepositoryPullRequestCreateForm, {
+ RepositoryPullRequestCreateFormVariant,
+} from "../../islands/RepositoryPullRequestCreateForm";
export interface RepositoryPullRequestCreateViewProps extends CommonProps {
errorMessage?: string | null;
initialValues?: PullRequest;
parentOrg: Organization;
repo: Repository;
+ variant: RepositoryPullRequestCreateFormVariant;
}
const RepositoryPullRequestCreateView: ReactView<RepositoryPullRequestCreateViewProps> =
- ({ commonProps, parentOrg, repo }) => {
+ ({ commonProps, parentOrg, repo, variant }) => {
return (
<Layout {...commonProps}>
<PageWrapper>
@@ -29,7 +33,11 @@ const RepositoryPullRequestCreateView: ReactView<RepositoryPullRequestCreateView
repo={repo}
/>
</IslandWrapper>
- <Grid.Col fluid style={{ marginTop: 32 }}></Grid.Col>
+ <IslandWrapper
+ data-islandid={`${RepositoryPullRequestCreateForm.name}$$0`}
+ >
+ <RepositoryPullRequestCreateForm variant={variant} />
+ </IslandWrapper>
</PageWrapper>
</Layout>
);