GitFOSS
.ts
TypeScript
(application/typescript)
// 1st-party
import type { ReactIsland } from "@ethicdevs/react-monolith";
// 3rd-party
import React, { useCallback, useEffect, useMemo, useState } from "react";
// generated via script[generate:prisma]
import { Organization } from "@prisma/client";
// app
import { RepositoryWithParentAndForkedFromRepos } from "../types";

export interface PullRequestSourceSelection {
  selectedOrg: {
    org: Organization;
    repos: {
      [repoSlug: string]: {
        repo: RepositoryWithParentAndForkedFromRepos;
        branches: string[];
      };
    };
  };
  selectedRepo: {
    repo: RepositoryWithParentAndForkedFromRepos;
    branches: string[];
  };
  selectedBranch: string;
}

export interface PullRequestSourceSelectionChangeEvent {
  data: PullRequestSourceSelection;
}

export interface PullRequestSourceSelectProps {
  defaultSource: {
    org: Organization;
    repo: RepositoryWithParentAndForkedFromRepos;
    branch: string;
  };
  sources: {
    [orgSlug: string]: {
      org: Organization;
      repos: {
        [repoKey: string]: {
          repo: RepositoryWithParentAndForkedFromRepos;
          branches: string[];
        };
      };
    };
  };
  namePrefix: "source" | "target";
  onSelectionChange?: (
    ev: PullRequestSourceSelectionChangeEvent
  ) => void | Promise<void>;
}

const PullRequestSourceSelect: ReactIsland<PullRequestSourceSelectProps> = ({
  defaultSource,
  sources,
  namePrefix,
  onSelectionChange,
}) => {
  const [selectedOrgSlug, setSelectedOrgSlug] = useState<string>(
    defaultSource.org.slug
  );
  const [selectedRepoSlug, setSelectedRepoSlug] = useState<string>(
    defaultSource.repo.slug
  );
  const [selectedBranch, setSelectedBranch] = useState<string>(
    defaultSource.branch
  );

  const allOrganizations = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    return orgsBySlug.reduce((acc, [_, { org }]) => {
      acc = [
        ...acc,
        {
          key: org.slug,
          value: org.displayName || org.slug,
        },
      ];
      return acc;
    }, [] as { key: string; value: string }[]);
  }, [sources]);

  const allReposBySelectedOrgSlug = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const org = orgsBySlug.find(([_, o]) => o.org.slug === selectedOrgSlug);
    if (org == null) return [];
    const [_selectedOrgSlug, { repos: orgRepositories }] = org;
    const reposBySlug = Object.entries(orgRepositories);
    return reposBySlug.reduce((acc, [_, { repo }]) => {
      acc = [
        ...acc,
        {
          key: repo.slug,
          value: repo.displayName || repo.slug,
        },
      ];
      return acc;
    }, [] as { key: string; value: string }[]);
  }, [sources, selectedOrgSlug]);

  const allBranchesBySelectedOrgAndRepoSlugs = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const org = orgsBySlug.find(([_, o]) => o.org.slug === selectedOrgSlug);
    if (org == null) return [];
    const [_selectedOrgSlug, { repos: orgRepositories }] = org;
    const reposByKey = Object.entries(orgRepositories);
    const repo = reposByKey.find(
      (r) =>
        r[1].repo.organization.slug === selectedOrgSlug &&
        r[1].repo.slug === selectedRepoSlug
    );
    if (repo == null) return [];
    return repo[1].branches.map((branch) => ({
      key: branch,
      value: branch,
    }));
  }, [sources, selectedOrgSlug, selectedRepoSlug]);

  const selectedOrg = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const org = orgsBySlug.find(([_, o]) => o.org.slug === selectedOrgSlug);
    return org != null ? org[1] : null;
  }, [sources, selectedOrgSlug]);

  const selectedRepo = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const parentOrg = orgsBySlug.find(
      ([_, o]) => o.org.slug === selectedOrgSlug
    );
    if (parentOrg == null) return null;
    const reposBySlug = Object.entries(parentOrg[1].repos);
    const repo = reposBySlug.find(
      (r) =>
        r[1].repo.organization.slug === selectedOrgSlug &&
        r[1].repo.slug === selectedRepoSlug
    );
    if (repo == null) return null;
    return repo[1];
  }, [sources, selectedOrgSlug, selectedRepoSlug]);

  const selectedBranchResult = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const parentOrg = orgsBySlug.find(
      ([_, o]) => o.org.slug === selectedOrgSlug
    );
    if (parentOrg == null) return selectedBranch;
    const reposBySlug = Object.entries(parentOrg[1].repos);
    const repo = reposBySlug.find(
      (r) =>
        r[1].repo.organization.slug === selectedOrgSlug &&
        r[1].repo.slug === selectedRepoSlug
    );
    if (repo == null) return selectedBranch;
    const { branches } = repo[1];
    return selectedBranch || branches[0];
  }, [sources, selectedOrgSlug, selectedRepoSlug, selectedBranch]);

  const emitSelectionChangeEvent = useCallback(() => {
    if (
      onSelectionChange != null &&
      typeof onSelectionChange === "function" &&
      selectedOrg != null &&
      selectedRepo != null &&
      selectedBranchResult != null
    ) {
      onSelectionChange({
        data: {
          selectedOrg,
          selectedRepo,
          selectedBranch: selectedBranchResult,
        },
      });
    }
  }, [onSelectionChange, selectedOrg, selectedRepo, selectedBranchResult]);

  const onOrgSlugChange = useCallback(
    (ev: React.ChangeEvent<HTMLSelectElement>) => {
      if (ev == null) return;
      if (ev.target == null) return;
      if (ev.target.value == null) return;
      const orgSlug = ev.target.value;
      setSelectedOrgSlug(orgSlug);
      if (orgSlug in sources) {
        const { repos } = sources[orgSlug];
        if (Object.keys(repos).length >= 1) {
          const { repo, branches } = Object.values(repos)[0];
          setSelectedRepoSlug(repo.slug);
          if (branches.length >= 1) {
            setSelectedBranch(branches[0]);
          }
        }
      }
      emitSelectionChangeEvent();
    },
    [
      emitSelectionChangeEvent,
      sources,
      setSelectedOrgSlug,
      setSelectedRepoSlug,
      setSelectedBranch,
    ]
  );

  const onRepoSlugChange = useCallback(
    (ev: React.ChangeEvent<HTMLSelectElement>) => {
      if (ev == null) return;
      if (ev.target == null) return;
      if (ev.target.value == null) return;
      const repoSlug = ev.target.value;
      setSelectedRepoSlug(repoSlug);
      if (repoSlug in sources[selectedOrgSlug].repos) {
        const { branches } = sources[selectedOrgSlug].repos[repoSlug];
        if (branches.length >= 1) {
          setSelectedBranch(branches[0]);
        }
      }
      emitSelectionChangeEvent();
    },
    [
      emitSelectionChangeEvent,
      sources,
      selectedOrgSlug,
      setSelectedRepoSlug,
      setSelectedBranch,
    ]
  );

  const onBranchChange = useCallback(
    (ev: React.ChangeEvent<HTMLSelectElement>) => {
      if (ev == null) return;
      if (ev.target == null) return;
      if (ev.target.value == null) return;
      setSelectedBranch(ev.target.value);
      emitSelectionChangeEvent();
    },
    [emitSelectionChangeEvent, setSelectedBranch]
  );

  const keyValueToOptionMapFn = useMemo(
    () =>
      ({ key, value }: { key: string; value: string }, idx: number) =>
        (
          <option key={[key, idx].join(":")} value={key}>
            {value}
          </option>
        ),
    []
  );

  useEffect(() => {
    emitSelectionChangeEvent();
  }, []);

  return (
    <div>
      <select
        name={`${namePrefix}_parent_org_slug`}
        value={selectedOrgSlug}
        onChange={onOrgSlugChange}
      >
        {allOrganizations.map(keyValueToOptionMapFn)}
      </select>
      {" / "}
      <select
        name={`${namePrefix}_repository_slug`}
        value={selectedRepoSlug}
        onChange={onRepoSlugChange}
      >
        {allReposBySelectedOrgSlug.map(keyValueToOptionMapFn)}
      </select>
      {" / "}
      <select
        name={`${namePrefix}_repository_${
          namePrefix === "source" ? "from" : "dest"
        }_branch`}
        value={selectedBranch}
        onChange={onBranchChange}
      >
        {allBranchesBySelectedOrgAndRepoSlugs.map(keyValueToOptionMapFn)}
      </select>
    </div>
  );
};

PullRequestSourceSelect.displayName = "PullRequestSourceSelect";
export default PullRequestSourceSelect;

.ts
TypeScript
(application/typescript)
// 1st-party
import type { ReactIsland } from "@ethicdevs/react-monolith";
// 3rd-party
import React, { useCallback, useEffect, useMemo, useState } from "react";
// generated via script[generate:prisma]
import { Organization } from "@prisma/client";
// app
import { RepositoryWithParentAndForkedFromRepos } from "../types";

export interface PullRequestSourceSelection {
  selectedOrg: {
    org: Organization;
    repos: {
      [repoSlug: string]: {
        repo: RepositoryWithParentAndForkedFromRepos;
        branches: string[];
      };
    };
  };
  selectedRepo: {
    repo: RepositoryWithParentAndForkedFromRepos;
    branches: string[];
  };
  selectedBranch: string;
}

export interface PullRequestSourceSelectionChangeEvent {
  data: PullRequestSourceSelection;
}

export interface PullRequestSourceSelectProps {
  defaultSource: {
    org: Organization;
    repo: RepositoryWithParentAndForkedFromRepos;
    branch: string;
  };
  sources: {
    [orgSlug: string]: {
      org: Organization;
      repos: {
        [repoKey: string]: {
          repo: RepositoryWithParentAndForkedFromRepos;
          branches: string[];
        };
      };
    };
  };
  namePrefix: "source" | "target";
  onSelectionChange?: (
    ev: PullRequestSourceSelectionChangeEvent
  ) => void | Promise<void>;
}

const PullRequestSourceSelect: ReactIsland<PullRequestSourceSelectProps> = ({
  defaultSource,
  sources,
  namePrefix,
  onSelectionChange,
}) => {
  const [selectedOrgSlug, setSelectedOrgSlug] = useState<string>(
    defaultSource.org.slug
  );
  const [selectedRepoSlug, setSelectedRepoSlug] = useState<string>(
    defaultSource.repo.slug
  );
  const [selectedBranch, setSelectedBranch] = useState<string>(
    defaultSource.branch
  );

  const allOrganizations = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    return orgsBySlug.reduce((acc, [_, { org }]) => {
      acc = [
        ...acc,
        {
          key: org.slug,
          value: org.displayName || org.slug,
        },
      ];
      return acc;
    }, [] as { key: string; value: string }[]);
  }, [sources]);

  const allReposBySelectedOrgSlug = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const org = orgsBySlug.find(([_, o]) => o.org.slug === selectedOrgSlug);
    if (org == null) return [];
    const [_selectedOrgSlug, { repos: orgRepositories }] = org;
    const reposBySlug = Object.entries(orgRepositories);
    return reposBySlug.reduce((acc, [_, { repo }]) => {
      acc = [
        ...acc,
        {
          key: repo.slug,
          value: repo.displayName || repo.slug,
        },
      ];
      return acc;
    }, [] as { key: string; value: string }[]);
  }, [sources, selectedOrgSlug]);

  const allBranchesBySelectedOrgAndRepoSlugs = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const org = orgsBySlug.find(([_, o]) => o.org.slug === selectedOrgSlug);
    if (org == null) return [];
    const [_selectedOrgSlug, { repos: orgRepositories }] = org;
    const reposByKey = Object.entries(orgRepositories);
    const repo = reposByKey.find(
      (r) =>
        r[1].repo.organization.slug === selectedOrgSlug &&
        r[1].repo.slug === selectedRepoSlug
    );
    if (repo == null) return [];
    return repo[1].branches.map((branch) => ({
      key: branch,
      value: branch,
    }));
  }, [sources, selectedOrgSlug, selectedRepoSlug]);

  const selectedOrg = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const org = orgsBySlug.find(([_, o]) => o.org.slug === selectedOrgSlug);
    return org != null ? org[1] : null;
  }, [sources, selectedOrgSlug]);

  const selectedRepo = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const parentOrg = orgsBySlug.find(
      ([_, o]) => o.org.slug === selectedOrgSlug
    );
    if (parentOrg == null) return null;
    const reposBySlug = Object.entries(parentOrg[1].repos);
    const repo = reposBySlug.find(
      (r) =>
        r[1].repo.organization.slug === selectedOrgSlug &&
        r[1].repo.slug === selectedRepoSlug
    );
    if (repo == null) return null;
    return repo[1];
  }, [sources, selectedOrgSlug, selectedRepoSlug]);

  const selectedBranchResult = useMemo(() => {
    const orgsBySlug = Object.entries(sources);
    const parentOrg = orgsBySlug.find(
      ([_, o]) => o.org.slug === selectedOrgSlug
    );
    if (parentOrg == null) return selectedBranch;
    const reposBySlug = Object.entries(parentOrg[1].repos);
    const repo = reposBySlug.find(
      (r) =>
        r[1].repo.organization.slug === selectedOrgSlug &&
        r[1].repo.slug === selectedRepoSlug
    );
    if (repo == null) return selectedBranch;
    const { branches } = repo[1];
    return selectedBranch || branches[0];
  }, [sources, selectedOrgSlug, selectedRepoSlug, selectedBranch]);

  const emitSelectionChangeEvent = useCallback(() => {
    if (
      onSelectionChange != null &&
      typeof onSelectionChange === "function" &&
      selectedOrg != null &&
      selectedRepo != null &&
      selectedBranchResult != null
    ) {
      onSelectionChange({
        data: {
          selectedOrg,
          selectedRepo,
          selectedBranch: selectedBranchResult,
        },
      });
    }
  }, [onSelectionChange, selectedOrg, selectedRepo, selectedBranchResult]);

  const onOrgSlugChange = useCallback(
    (ev: React.ChangeEvent<HTMLSelectElement>) => {
      if (ev == null) return;
      if (ev.target == null) return;
      if (ev.target.value == null) return;
      const orgSlug = ev.target.value;
      setSelectedOrgSlug(orgSlug);
      if (orgSlug in sources) {
        const { repos } = sources[orgSlug];
        if (Object.keys(repos).length >= 1) {
          const { repo, branches } = Object.values(repos)[0];
          setSelectedRepoSlug(repo.slug);
          if (branches.length >= 1) {
            setSelectedBranch(branches[0]);
          }
        }
      }
      emitSelectionChangeEvent();
    },
    [
      emitSelectionChangeEvent,
      sources,
      setSelectedOrgSlug,
      setSelectedRepoSlug,
      setSelectedBranch,
    ]
  );

  const onRepoSlugChange = useCallback(
    (ev: React.ChangeEvent<HTMLSelectElement>) => {
      if (ev == null) return;
      if (ev.target == null) return;
      if (ev.target.value == null) return;
      const repoSlug = ev.target.value;
      setSelectedRepoSlug(repoSlug);
      if (repoSlug in sources[selectedOrgSlug].repos) {
        const { branches } = sources[selectedOrgSlug].repos[repoSlug];
        if (branches.length >= 1) {
          setSelectedBranch(branches[0]);
        }
      }
      emitSelectionChangeEvent();
    },
    [
      emitSelectionChangeEvent,
      sources,
      selectedOrgSlug,
      setSelectedRepoSlug,
      setSelectedBranch,
    ]
  );

  const onBranchChange = useCallback(
    (ev: React.ChangeEvent<HTMLSelectElement>) => {
      if (ev == null) return;
      if (ev.target == null) return;
      if (ev.target.value == null) return;
      setSelectedBranch(ev.target.value);
      emitSelectionChangeEvent();
    },
    [emitSelectionChangeEvent, setSelectedBranch]
  );

  const keyValueToOptionMapFn = useMemo(
    () =>
      ({ key, value }: { key: string; value: string }, idx: number) =>
        (
          <option key={[key, idx].join(":")} value={key}>
            {value}
          </option>
        ),
    []
  );

  useEffect(() => {
    emitSelectionChangeEvent();
  }, []);

  return (
    <div>
      <select
        name={`${namePrefix}_parent_org_slug`}
        value={selectedOrgSlug}
        onChange={onOrgSlugChange}
      >
        {allOrganizations.map(keyValueToOptionMapFn)}
      </select>
      {" / "}
      <select
        name={`${namePrefix}_repository_slug`}
        value={selectedRepoSlug}
        onChange={onRepoSlugChange}
      >
        {allReposBySelectedOrgSlug.map(keyValueToOptionMapFn)}
      </select>
      {" / "}
      <select
        name={`${namePrefix}_repository_${
          namePrefix === "source" ? "from" : "dest"
        }_branch`}
        value={selectedBranch}
        onChange={onBranchChange}
      >
        {allBranchesBySelectedOrgAndRepoSlugs.map(keyValueToOptionMapFn)}
      </select>
    </div>
  );
};

PullRequestSourceSelect.displayName = "PullRequestSourceSelect";
export default PullRequestSourceSelect;

GitFOSS • v0.2.0 (#421408f) • MIT License