diff --git a/components/AddContributorsHeader/add-contributors-header.tsx b/components/AddContributorsHeader/add-contributors-header.tsx deleted file mode 100644 index 14e07f35e..000000000 --- a/components/AddContributorsHeader/add-contributors-header.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { FaPlus } from "react-icons/fa"; - -import clsx from "clsx"; - -import { useEffect, useState } from "react"; - -import Link from "next/link"; -import { MdOutlineArrowBackIos } from "react-icons/md"; -import Button from "components/shared/Button/button"; -import Search from "components/atoms/Search/search"; -import useDebounceTerm from "lib/hooks/useDebounceTerm"; - -interface AddContributorsHeaderProps { - selectedContributorsIds: number[]; - list: DbUserList; - workspaceId?: string; - onAddToList?: () => void; - loading?: boolean; - onSearch: (searchTerm: string | undefined) => void; - searchSuggestions?: string[]; - onSearchSelect?: (username: string) => void; -} - -const AddContributorsHeader = ({ - selectedContributorsIds, - list, - workspaceId, - onAddToList, - loading, - onSearch, - searchSuggestions, - onSearchSelect, -}: AddContributorsHeaderProps): JSX.Element => { - const [contributorSearch, setContributorSearch] = useState(""); - const debouncedSearchTerm = useDebounceTerm(contributorSearch, 300); - - useEffect(() => { - onSearch(debouncedSearchTerm); - }, [debouncedSearchTerm]); - - return ( -
-
-

- - - - {list.name} -

-
-
- 0 ? "bg-sauced-orange text-white" : "bg-light-slate-5 text-slate-400" - )} - > - {selectedContributorsIds.length} - -

- Contributors Selected -

-
- -
-
-
- -
-
- ); -}; - -export default AddContributorsHeader; diff --git a/components/Contributors/AddToContributorInsightDrawer.tsx b/components/Contributors/AddToContributorInsightDrawer.tsx index 1acb2ccf9..b10b76cc9 100644 --- a/components/Contributors/AddToContributorInsightDrawer.tsx +++ b/components/Contributors/AddToContributorInsightDrawer.tsx @@ -135,6 +135,7 @@ export default function AddToContributorInsightDrawer({ })), ]} value={selectedInsight ?? "new"} + labelText="Select a workspace" placeholder="Select a workspace" onValueChange={(value) => { setSelectedInsight(value); @@ -154,6 +155,7 @@ export default function AddToContributorInsightDrawer({ })), ]} value={workspaceId} + labelText="Select a workspace" placeholder="Select a workspace" onValueChange={(value) => { setWorkspaceId(value); diff --git a/components/Contributors/AddToContributorInsightModal.tsx b/components/Contributors/AddToContributorInsightModal.tsx index a5dc7d82e..7848cfe6a 100644 --- a/components/Contributors/AddToContributorInsightModal.tsx +++ b/components/Contributors/AddToContributorInsightModal.tsx @@ -136,6 +136,7 @@ export default function AddToContributorInsightModal({ })), ]} value={selectedInsight ?? "new"} + labelText="Select a workspace" placeholder="Select a workspace" onValueChange={(value) => { setSelectedInsight(value); @@ -155,6 +156,7 @@ export default function AddToContributorInsightModal({ })), ]} value={workspaceId} + labelText="Select a workspace" placeholder="Select a workspace" onValueChange={(value) => { setWorkspaceId(value); diff --git a/components/Repositories/AddToWorkspaceDrawer.tsx b/components/Repositories/AddToWorkspaceDrawer.tsx index 0f212d6fd..930d6eb49 100644 --- a/components/Repositories/AddToWorkspaceDrawer.tsx +++ b/components/Repositories/AddToWorkspaceDrawer.tsx @@ -137,6 +137,7 @@ export default function AddToWorkspaceDrawer({ repository, type = "repo" }: AddT ]} position="popper" value={workspaceId ?? "new"} + labelText="Select a workspace" placeholder="Select a workspace" onValueChange={(value) => { setWorkspaceId(value); diff --git a/components/Repositories/AddToWorkspaceModal.tsx b/components/Repositories/AddToWorkspaceModal.tsx index 7c7c2fe79..b880e4f9e 100644 --- a/components/Repositories/AddToWorkspaceModal.tsx +++ b/components/Repositories/AddToWorkspaceModal.tsx @@ -131,6 +131,7 @@ export default function AddToWorkspaceModal({ repository, isOpen, onCloseModal, })), ]} value={workspaceId ?? "new"} + labelText="Select a workspace" placeholder="Select a workspace" onValueChange={(value) => { setWorkspaceId(value); diff --git a/components/Workspaces/SearchReposTable.tsx b/components/Workspaces/SearchReposTable.tsx index 340359745..6615e51b5 100644 --- a/components/Workspaces/SearchReposTable.tsx +++ b/components/Workspaces/SearchReposTable.tsx @@ -62,7 +62,13 @@ export const SearchedReposTable = ({ event.preventDefault; }} > - + diff --git a/components/Workspaces/SearchedContributorsTable.tsx b/components/Workspaces/SearchedContributorsTable.tsx index 7c4383a9e..ff66e78ad 100644 --- a/components/Workspaces/SearchedContributorsTable.tsx +++ b/components/Workspaces/SearchedContributorsTable.tsx @@ -43,7 +43,13 @@ export const SearchedContributorsTable = ({ event.preventDefault; }} > - + diff --git a/components/Workspaces/TrackedContributorsWizard/SearchByContributorsStep.tsx b/components/Workspaces/TrackedContributorsWizard/SearchByContributorsStep.tsx index c0cc614b4..193624ef0 100644 --- a/components/Workspaces/TrackedContributorsWizard/SearchByContributorsStep.tsx +++ b/components/Workspaces/TrackedContributorsWizard/SearchByContributorsStep.tsx @@ -81,6 +81,7 @@ export const SearchByContributorsStep = ({ > { diff --git a/components/Workspaces/WorkspaceMembersConfig/workspace-members-config.tsx b/components/Workspaces/WorkspaceMembersConfig/workspace-members-config.tsx index 207a6ef86..65f2c9286 100644 --- a/components/Workspaces/WorkspaceMembersConfig/workspace-members-config.tsx +++ b/components/Workspaces/WorkspaceMembersConfig/workspace-members-config.tsx @@ -62,6 +62,7 @@ const WorkspaceMembersConfig = ({ value={username} onChange={(value) => handleChange(value)} placeholder="Enter username" + labelText="Enter full username" name="search" className="flex-1 text-base" /> diff --git a/components/atoms/Search/search.tsx b/components/atoms/Search/search.tsx index b9d4fe922..8444ab62a 100644 --- a/components/atoms/Search/search.tsx +++ b/components/atoms/Search/search.tsx @@ -24,6 +24,7 @@ interface SearchProps { onSelect?: (value: string) => void; isLoading?: boolean; isDisabled?: boolean; + labelText: string; } const suggestionsStyle = { @@ -45,6 +46,7 @@ const Search = ({ isLoading, onSelect, isDisabled, + labelText, }: SearchProps): JSX.Element => { const [cursor, setCursor] = useState(-1); const [search, setSearch] = useState(value); @@ -111,11 +113,12 @@ const Search = ({ // using min-w-[15rem] to take into account the width of the X icon when it gets rendered // to avoid the search input from expanding when someone starts typing. return ( -
+ {labelText} { if (e.code === "Enter") { @@ -176,7 +178,7 @@ const Search = ({ )} )} -
+ ); }; export default Search; diff --git a/components/atoms/Select/single-select.tsx b/components/atoms/Select/single-select.tsx index 1672ccdfe..f78c473c8 100644 --- a/components/atoms/Select/single-select.tsx +++ b/components/atoms/Select/single-select.tsx @@ -15,6 +15,7 @@ interface SingleSelectProps { position?: "popper" | "item-aligned"; isSearchable?: boolean; insetLabel?: string; + labelText: string; } const SingleSelect = ({ @@ -26,6 +27,7 @@ const SingleSelect = ({ inputPlaceholder, isSearchable = false, insetLabel, + labelText, }: SingleSelectProps) => { const inputRef = useRef(null); const [inputValue, setInputValue] = useState(""); @@ -64,8 +66,9 @@ const SingleSelect = ({ insetLabel && `before:content-[attr(data-inset-label)] before:mr-1 before:font-normal before:text-slate-500` )} > - -
+ {labelText} + diff --git a/components/atoms/Select/single.select.stories.tsx b/components/atoms/Select/single.select.stories.tsx index e6a207286..c32ef320d 100644 --- a/components/atoms/Select/single.select.stories.tsx +++ b/components/atoms/Select/single.select.stories.tsx @@ -38,6 +38,7 @@ const baseProps: ComponentProps = { // eslint-disable-next-line no-console console.log(value); }, + labelText: "Accessible label", }; export const Default: Story = { diff --git a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx index bc62c3797..a1aa47fa6 100644 --- a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx +++ b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx @@ -794,6 +794,7 @@ const ContributorHighlightCard = ({ setContributorSearch(value)} diff --git a/components/molecules/TableHeader/table-header.tsx b/components/molecules/TableHeader/table-header.tsx index f440cd7b2..e0b59a92b 100644 --- a/components/molecules/TableHeader/table-header.tsx +++ b/components/molecules/TableHeader/table-header.tsx @@ -88,6 +88,7 @@ const TableHeader = ({ title, metaInfo, entity, onSearch, layout, onLayoutToggle {onSearch ? ( handleChange(value)} placeholder="Enter email address" + labelText="Enter email address" name="search" className="flex-1 text-base" /> diff --git a/components/organisms/InsightPage/InsightPage.tsx b/components/organisms/InsightPage/InsightPage.tsx deleted file mode 100644 index 5d9ab874d..000000000 --- a/components/organisms/InsightPage/InsightPage.tsx +++ /dev/null @@ -1,723 +0,0 @@ -import { useEffect, useState } from "react"; -import { useRouter } from "next/router"; -import dynamic from "next/dynamic"; - -import { useDebounce } from "rooks"; -import Button from "components/shared/Button/button"; -import TextInput from "components/atoms/TextInput/text-input"; -import Text from "components/atoms/Typography/text"; -import Title from "components/atoms/Typography/title"; -import RepositoriesCart from "components/organisms/RepositoriesCart/repositories-cart"; -import RepositoryCartItem from "components/molecules/ReposoitoryCartItem/repository-cart-item"; -import RepoNotIndexed from "components/organisms/Repositories/repository-not-indexed"; -import useRepositories from "lib/hooks/api/useRepositories"; - -import useSupabaseAuth from "lib/hooks/useSupabaseAuth"; -import { generateRepoParts, getAvatarByUsername } from "lib/utils/github"; -import Error from "components/atoms/Error/Error"; -import Search from "components/atoms/Search/search"; -import { useToast } from "lib/hooks/useToast"; -import { useFetchInsightRecommendedRepositories } from "lib/hooks/useFetchOrgRecommendations"; -import { RepoCardProfileProps } from "components/molecules/RepoCardProfile/repo-card-profile"; -import SingleSelect from "components/atoms/Select/single-select"; -import { fetchApiData } from "helpers/fetchApiData"; -import { useGetUserWorkspaces } from "lib/hooks/api/useGetUserWorkspaces"; -import SuggestedRepositoriesList from "../SuggestedRepoList/suggested-repo-list"; - -// lazy import DeleteInsightPageModal and TeamMembersConfig component to optimize bundle size they don't load on initial render -const DeleteInsightPageModal = dynamic(() => import("./DeleteInsightPageModal")); -const TransferInsightModal = dynamic(() => import("components/Workspaces/TransferInsightModal")); - -const enum RepoLookupError { - Initial = 0, - NotIndexed = 1, - Invalid = 3, - Error = 4, -} - -const enum OrgLookupError { - Initial = 0, - Invalid = 1, - Error = 2, -} - -interface InsightPageProps { - edit?: boolean; - insight?: DbUserInsight; - pageRepos?: DbRepo[]; - workspaceId?: string; -} -const staticSuggestedRepos: RepoCardProfileProps[] = [ - { - avatar: "https://avatars.githubusercontent.com/u/57568598?s=200&v=4", - prCount: 8, - repoName: "app", - issueCount: 87, - orgName: "open-sauced", - }, - { - avatar: "https://avatars.githubusercontent.com/u/59704711?s=200&v=4", - prCount: 26, - repoName: "cli", - issueCount: 398, - orgName: "cli", - }, - { - avatar: "https://avatars.githubusercontent.com/u/42048915?s=200&v=4", - prCount: 100, - repoName: "deno", - issueCount: 1200, - orgName: "denoland", - }, -]; - -const InsightPage = ({ edit, insight, pageRepos, workspaceId }: InsightPageProps) => { - const { sessionToken, providerToken, user } = useSupabaseAuth(); - - const { toast } = useToast(); - const router = useRouter(); - const pageHref = router.asPath; - const [reposIds, setReposIds] = useState([]); - const { data: repoListData } = useRepositories(reposIds); - - useEffect(() => { - const searchParams = new URLSearchParams(pageHref.substring(pageHref.indexOf("?"))); - if (router.query.selectedRepos) { - setRepos(JSON.parse(router.query.selectedRepos as string) || []); - } else if (searchParams.has("selectedReposIDs")) { - setReposIds(JSON.parse(searchParams.get("selectedReposIDs") as string) || []); - setRepos(repoListData); - } - }, [repoListData, router.query.selectedRepos, pageHref]); - - const { data: recommendedRepos, isLoading } = useFetchInsightRecommendedRepositories(); - - // Loading States - const [deleteLoading, setDeleteLoading] = useState(false); - const [createLoading, setCreateLoading] = useState(false); - const [addRepoLoading, setAddRepoLoading] = useState({ repoName: "", isAddedFromCart: false, isLoading: false }); - - const [name, setName] = useState(insight?.name || ""); - const [organization, setOrganization] = useState(""); - const [isNameValid, setIsNameValid] = useState(false); - const [submitted, setSubmitted] = useState(false); - const [repos, setRepos] = useState([]); - const [repoHistory, setRepoHistory] = useState([]); - const [addRepoError, setAddRepoError] = useState(RepoLookupError.Initial); - const [syncOrganizationError, setSyncOrganizationError] = useState(OrgLookupError.Initial); - const [isModalOpen, setIsModalOpen] = useState(false); - const [repoSearchTerm, setRepoSearchTerm] = useState(""); - const [suggestions, setSuggestions] = useState([]); - - const { data: workspacesData, isLoading: workspacesLoading } = useGetUserWorkspaces(); - const [options, setOptions] = useState<{ label: string; value: string }[]>([]); - const [selectedWorkspace, setSelectedWorkspace] = useState(workspaceId!); - const [isTransferModalOpen, setIsTransferModalOpen] = useState(false); - - useEffect(() => { - if (workspaceId && !workspacesLoading) { - const filteredWorkspaces = workspacesData?.data?.filter((workspace) => - workspace.members.find( - (member) => member.user_id === Number(user?.user_metadata.sub) && ["owner", "editor"].includes(member.role) - ) - ); - - setOptions( - Array.from(filteredWorkspaces!, (workspace) => { - return { label: workspace.name, value: workspace.id }; - }) - ); - } - }, [workspacesData]); - - const recommendedReposWithoutSelected = - recommendedRepos && recommendedRepos.length > 0 - ? recommendedRepos - .filter((repo) => !repos.find((selectedRepo) => selectedRepo.id === repo.id)) - .map((repo) => { - const [orgName, repoName] = repo.full_name.split("/"); - const totalPrs = (repo.open_prs_count || 0) + (repo.closed_prs_count || 0) + (repo.merged_prs_count || 0); - const avatar = getAvatarByUsername(orgName, 60); - const totalIssues = repo.issues || 0; - - return { - orgName, - repoName, - totalPrs, - avatar, - totalIssues, - }; - }) - .slice(0, 3) - : staticSuggestedRepos; - - useEffect(() => { - if (pageRepos) { - setRepos(pageRepos); - } - }, [pageRepos, insight?.is_public]); - - const reposRemoved = repoHistory.map((repo) => { - const [repoOwner, repoName] = repo.full_name.split("/"); - const totalPrs = - (repo.open_prs_count || 0) + - (repo.closed_prs_count || 0) + - (repo.merged_prs_count || 0) + - (repo.draft_prs_count || 0); - - return { - orgName: repoOwner, - repoName: repoName, - totalPrs, - avatar: getAvatarByUsername(repoOwner, 60), - handleRemoveItem: () => {}, - }; - }); - - const validateName = (name: string) => { - if (!name || name.trim().length <= 3) return false; - - return true; - }; - - const handleOnNameChange = (value: string) => { - setName(value); - setIsNameValid(validateName(value)); - }; - - const handleOnOrganizationChange = (value: string) => { - setOrganization(value); - }; - - const disableCreateButton = () => { - if ((insight?.name && validateName(name)) || (repos.length && validateName(name))) return false; - if (submitted) return true; - if (!isNameValid) return true; - - return false; - }; - - const handleCreateInsightPage = async () => { - setSubmitted(true); - setCreateLoading(true); - - if (!sessionToken || !user) { - toast({ description: "You must be logged in to create a page", variant: "danger" }); - return; - } - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/${workspaceId ? `workspaces/${workspaceId}` : user}/insights`, - { - method: "POST", - headers: { - "Content-type": "application/json", - Authorization: `Bearer ${sessionToken}`, - }, - body: JSON.stringify({ - name, - repos: repos.map((repo) => ({ id: repo.id, fullName: repo.full_name })), - is_public: true, - }), - } - ); - setCreateLoading(false); - if (response.ok) { - const { insight_id } = await response.json(); - toast({ description: "Page created successfully", variant: "success" }); - router.push(`/workspaces/${workspaceId}/repository-insights/${insight_id}/dashboard`); - } - - setSubmitted(false); - }; - - const handleUpdateInsightPage = async () => { - setSubmitted(true); - setCreateLoading(true); - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/workspaces/${workspaceId}/insights/${insight?.id}`, - { - method: "PATCH", - headers: { - "Content-type": "application/json", - Authorization: `Bearer ${sessionToken}`, - }, - body: JSON.stringify({ - name, - repos: repos.map((repo) => ({ id: repo.id, fullName: repo.full_name })), - // eslint-disable-next-line - is_public: true, - }), - } - ); - setCreateLoading(false); - if (response && response.ok) { - toast({ description: "Page updated successfully", variant: "success" }); - router.push(`/workspaces/${workspaceId}/repository-insights/${insight?.id}/dashboard`); - } else { - toast({ description: "An error occurred!", variant: "danger" }); - } - - setSubmitted(false); - }; - - const addSuggestedRepo = (repoToAdd: string) => { - const hasRepo = repos.find((repo) => `${repo.full_name}` === repoToAdd); - - if (hasRepo) { - return; - } - - const actualRepo = recommendedRepos?.find((repo) => repo.full_name === repoToAdd); - - if (!actualRepo) { - loadAndAddRepo(repoToAdd); - return; - } - - setRepos((repos) => { - return [...repos, actualRepo as unknown as DbRepo]; - }); - }; - - const loadAndAddRepo = async (repoToAdd: string, isAddedFromCart = false) => { - setAddRepoError(RepoLookupError.Initial); - - const hasRepo = repos.find((repo) => `${repo.full_name}` === repoToAdd); - - if (hasRepo) { - return; - } - - const { apiPaths, isValidUrl } = generateRepoParts(repoToAdd); - - if (!isValidUrl) { - setAddRepoError(RepoLookupError.Invalid); - return; - } - - const { repoFullName } = apiPaths; - - setAddRepoLoading({ repoName: repoToAdd, isAddedFromCart, isLoading: true }); - try { - const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/repos/${repoFullName}`); - setAddRepoLoading({ repoName: repoToAdd, isAddedFromCart, isLoading: false }); - - if (response.ok) { - const addedRepo = (await response.json()) as DbRepo; - - setRepos((repos) => { - return [...repos, addedRepo]; - }); - setAddRepoError(RepoLookupError.Initial); - setRepoSearchTerm(""); - } else { - const publicRepoResponse = await fetch(`https://api.github.com/repos/${repoFullName}`); - - if (publicRepoResponse.ok) { - const publicRepo = await publicRepoResponse.json(); - - // create a stub repo to send to API - const addedRepo = { - id: publicRepo.id, - full_name: publicRepo.full_name, - } as DbRepo; - - setRepos((repos) => { - return [...repos, addedRepo]; - }); - setAddRepoError(RepoLookupError.Initial); - setRepoSearchTerm(""); - } else { - setAddRepoError(RepoLookupError.Invalid); - } - } - } catch { - setAddRepoLoading({ repoName: repoToAdd, isAddedFromCart, isLoading: false }); - setAddRepoError(RepoLookupError.Error); - } - }; - - const handleAddRepository = async () => { - await loadAndAddRepo(repoSearchTerm); - }; - - const handleReAddRepository = async (repoAdded: string) => { - const existInSuggestions = recommendedRepos.find((repo) => `${repo.full_name}` === repoAdded); - - if (existInSuggestions) { - setRepoHistory((historyRepos) => { - return historyRepos.filter((repo) => `${repo.full_name}` !== repoAdded); - }); - - addSuggestedRepo(repoAdded); - return; - } - - try { - await loadAndAddRepo(repoAdded, true); - - setRepoHistory((historyRepos) => { - return historyRepos.filter((repo) => `${repo.full_name}` !== repoAdded); - }); - } catch (e) {} - }; - - const handleRemoveRepository = (id: string) => { - setRepos((addedRepos) => { - return addedRepos.filter((repo) => repo.id !== id); - }); - - const repoRemoved = repos.find((repo) => repo.id === id); - - const repoAlreadyInHistory = repoHistory.find((repo) => repo.id === id); - if (repoAlreadyInHistory) { - return; - } - - setRepoHistory((historyRepos) => { - return [...historyRepos, repoRemoved as DbRepo]; - }); - }; - - const getRepoLookupError = (code: RepoLookupError) => { - if (code === RepoLookupError.Error) { - return ; - } - - if (code === RepoLookupError.Invalid) { - return ; - } - - if (code === RepoLookupError.NotIndexed) { - return ; - } - - return null; - }; - - const getOrganizationLookupError = (code: OrgLookupError) => { - if (code === OrgLookupError.Error) { - return ; - } - - if (code === OrgLookupError.Invalid) { - return ; - } - - return <>; - }; - - const handleDeleteInsightPage = async () => { - setSubmitted(true); - setDeleteLoading(true); - const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/insights/${insight?.id}`, { - method: "DELETE", - headers: { - "Content-type": "application/json", - Authorization: `Bearer ${sessionToken}`, - }, - }); - - setDeleteLoading(false); - if (response.ok) { - toast({ description: "Page deleted successfully!", variant: "success" }); - setIsModalOpen(false); - router.push(`/workspaces/${workspaceId}/repository-insights`); - } - - setSubmitted(false); - }; - - const handleOnModalClose = () => { - setIsModalOpen(false); - }; - - const updateSuggestionsDebounced = useDebounce(async () => { - setCreateLoading(true); - - const req = await fetch( - `https://api.github.com/search/repositories?q=${encodeURIComponent( - `${repoSearchTerm} in:name in:repo:owner/name sort:updated` - )}`, - { - ...(providerToken - ? { - headers: { - Authorization: `Bearer ${providerToken}`, - }, - } - : {}), - } - ); - - setCreateLoading(false); - if (req.ok) { - const res = await req.json(); - const suggestions = res.items.map((item: any) => item.full_name); - setSuggestions(suggestions); - } - }, 250); - - const handleAddOrganizationRepositories = async () => { - setSyncOrganizationError(OrgLookupError.Initial); - - const orgReposResponse = await fetch( - `https://api.github.com/orgs/${organization}/repos?type=public&sort=pushed&direction=desc`, - { - headers: providerToken - ? { - Authorization: `Bearer ${providerToken}`, - } - : {}, - } - ); - - if (orgReposResponse.ok) { - const orgReposData = await orgReposResponse.json(); - - // create a stub repo to send to API - const orgRepos = orgReposData - .filter( - (orgRepo: { id: number; full_name: string }) => !repos.find((repo) => orgRepo.full_name === repo.full_name) - ) - .slice(0, 10) - .map((orgRepo: { id: number; full_name: string }) => ({ - id: orgRepo.id, - full_name: orgRepo.full_name, - })) as DbRepo[]; - - setRepos((repos) => { - return [...repos, ...orgRepos]; - }); - - setSyncOrganizationError(OrgLookupError.Initial); - setOrganization(""); - } else { - if (orgReposResponse.status === 404) { - setSyncOrganizationError(OrgLookupError.Invalid); - } else { - setSyncOrganizationError(OrgLookupError.Error); - } - } - }; - - const transferWorkspace = async () => { - const selectedOption = options.find((opt) => opt.value === selectedWorkspace); - const response = await fetchApiData({ - method: "POST", - path: `workspaces/${workspaceId!}/insights/${selectedWorkspace}`, - body: { - id: insight?.id, - }, - bearerToken: sessionToken!, - pathValidator: () => true, - }); - if (response.error) { - toast({ description: "An error has occurred. Try again.", variant: "success" }); - return; - } - - toast({ description: `Moved insight to ${selectedOption?.label}`, variant: "success" }); - router.push(`/workspaces/${selectedWorkspace}/repository-insights/${insight?.id}/dashboard`); - }; - - useEffect(() => { - setSuggestions([]); - if (!repoSearchTerm) return; - updateSuggestionsDebounced(); - }, [repoSearchTerm]); - - return ( -
-
-
- - {edit ? "Update" : "Create New"} {workspaceId ? "Repository Insight" : "Insight Page"} - - - An insight page is a dashboard containing selected repositories that you and your team can get insights - from. - -
- -
- - Page Name - - - -
- -
- - Sync GitHub Organization - - -
- -
-
- -
- -
{getOrganizationLookupError(syncOrganizationError)}
-
- -
- - Add Repository - - setRepoSearchTerm(value)} - onSearch={(search) => setRepoSearchTerm(search as string)} - /> - -
- -
- -
- { - addSuggestedRepo(repo); - }} - /> -
-
- -
{getRepoLookupError(addRepoError)}
- - {edit && ( -
- - Danger Zone - - -
- {workspaceId && ( -
-
- Transfer to other Workspace - Move this insight to another workspace where you are an owner or editor. -
- opt.value === workspaceId)?.label} - options={options} - onValueChange={(value: string) => { - setSelectedWorkspace(value); - }} - /> - -
- )} - - Delete Page - - Once you delete a page, you're past the point of no return. - -
- -
-
-
- )} -
- -
- 0} - handleCreatePage={handleCreateInsightPage} - handleUpdatePage={handleUpdateInsightPage} - handleAddToCart={handleReAddRepository} - history={reposRemoved.filter( - (repo) => !repos.find((r) => r.full_name === `${repo.orgName}/${repo.repoName}`) - )} - createPageButtonDisabled={disableCreateButton()} - > - {repos.map((repo) => { - const [repoOwner, repoName] = repo.full_name.split("/"); - const totalPrs = - (repo.open_prs_count || 0) + - (repo.closed_prs_count || 0) + - (repo.merged_prs_count || 0) + - (repo.draft_prs_count || 0); - - return ( - handleRemoveRepository(repo.id)} - orgName={repoOwner} - repoName={repoName} - totalPrs={totalPrs} - /> - ); - })} - -
- - {workspaceId && ( - setIsTransferModalOpen(false)} - handleTransfer={transferWorkspace} - insightName={insight?.name || ""} - currentWorkspaceName={options.find((opt) => opt.value === workspaceId)?.label || ""} - destinationWorkspaceName={options.find((opt) => opt.value === selectedWorkspace)?.label || ""} - /> - )} - - -
- ); -}; - -export default InsightPage; diff --git a/components/organisms/UserSettingsPage/user-settings-page.tsx b/components/organisms/UserSettingsPage/user-settings-page.tsx index b348c99bb..b7e211aef 100644 --- a/components/organisms/UserSettingsPage/user-settings-page.tsx +++ b/components/organisms/UserSettingsPage/user-settings-page.tsx @@ -336,11 +336,12 @@ const UserSettingsPage = ({ user }: UserSettingsPageProps) => {
-
-