diff --git a/prisma/migrations/20241223221420_github_installation_id/migration.sql b/prisma/migrations/20241223221420_github_installation_id/migration.sql new file mode 100644 index 0000000..0cb50fc --- /dev/null +++ b/prisma/migrations/20241223221420_github_installation_id/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Project" ADD COLUMN "githubInstallationId" TEXT, +ALTER COLUMN "inviteCode" SET DEFAULT floor(random() * 90000000 + 10000000)::text; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2aa17ac..8196bee 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -33,15 +33,16 @@ model Session { } model Project { - id String @id @default(cuid()) - name String - description String - github String? - customData String[] - rateLimitReq Int @default(5) - rateLimitTime Int @default(60) + id String @id @default(cuid()) + name String + description String + github String? + githubInstallationId String? + customData String[] + rateLimitReq Int @default(5) + rateLimitTime Int @default(60) // 8 digit random number - inviteCode String @unique @default(dbgenerated("floor(random() * 90000000 + 10000000)::text")) + inviteCode String @unique @default(dbgenerated("floor(random() * 90000000 + 10000000)::text")) users User[] @relation("UserProjects") feedback Feedback[] diff --git a/src/components/app/GithubRepoChooser/GithubRepoChooser.tsx b/src/components/app/GithubRepoChooser/GithubRepoChooser.tsx index 2a565d1..73c6338 100644 --- a/src/components/app/GithubRepoChooser/GithubRepoChooser.tsx +++ b/src/components/app/GithubRepoChooser/GithubRepoChooser.tsx @@ -20,7 +20,7 @@ import { getRepos } from './getRepos'; export default function GithubRepoChooser(props: Props) { const [open, setOpen] = React.useState(false); const [value, setValue] = React.useState(''); - const [repos, setRepos] = React.useState([]); + const [repos, setRepos] = React.useState<{ name: string, installationId: string }[]>([]); const [isLoading, setIsLoading] = React.useState(true); const [displayText, setDisplayText] = React.useState('Select a repository'); @@ -30,16 +30,22 @@ export default function GithubRepoChooser(props: Props) { if (response.success) { setRepos(response.repos!); setIsLoading(false); + if (props.selected) { + setValue(props.selected); + } } }); - setValue(props.selected ?? ''); }, []); React.useEffect(() => { - props.onSelect(value); - }, [value]); + if (isLoading || !value) return; + const repo = repos.find((repo) => repo.name === value); + if (repo) { + props.onSelect(value, repo.installationId); + } + }, [value, repos, isLoading, props.onSelect]); React.useEffect(() => { - if (value.length > 0) { - setDisplayText(repos.find((repo) => repo === value)!); + if (value.length > 0 && !isLoading) { + setDisplayText(repos.find((repo) => repo.name === value)!.name); } else if (repos.length === 0) { setDisplayText('No repositories found'); } else { @@ -69,16 +75,15 @@ export default function GithubRepoChooser(props: Props) { {repos.map((repo) => ( { - console.log(currentValue, value); - setValue(currentValue === value ? '' : currentValue); + setValue(currentValue); setOpen(false); }} > - {repo} - + {repo.name} + ))} @@ -90,6 +95,6 @@ export default function GithubRepoChooser(props: Props) { } interface Props { - onSelect: (repo: string) => void; + onSelect: (repo: string, installationId: string) => void; selected?: string; } diff --git a/src/components/app/GithubRepoChooser/getRepos.ts b/src/components/app/GithubRepoChooser/getRepos.ts index 9cfcb0b..67043d6 100644 --- a/src/components/app/GithubRepoChooser/getRepos.ts +++ b/src/components/app/GithubRepoChooser/getRepos.ts @@ -9,7 +9,7 @@ export async function getRepos() { return { success: false, error: 'You must be logged in' }; } - const repoList: Array<{ name: string; pushed_at: string }> = []; + const repoList: Array<{ name: string; pushed_at: string; installationId: string }> = []; for (const installation of user.installations) { const octokit = await octokitApp.getInstallationOctokit(Number(installation)); @@ -28,6 +28,7 @@ export async function getRepos() { const repoData = repos.repositories.map((repo) => ({ name: repo.full_name, pushed_at: repo.pushed_at ?? '1970-01-01T00:00:00Z', + installationId: installation, })); repoList.push(...repoData); @@ -35,7 +36,9 @@ export async function getRepos() { const sortedRepos = repoList .sort((a, b) => new Date(b.pushed_at).getTime() - new Date(a.pushed_at).getTime()) - .map((repo) => repo.name); + .map((repo) => { + return { name: repo.name, installationId: repo.installationId }; + }); return { success: true, repos: sortedRepos }; } diff --git a/src/components/app/ProjectSettings/ProjectSettings.tsx b/src/components/app/ProjectSettings/ProjectSettings.tsx index 7519129..af93f8b 100644 --- a/src/components/app/ProjectSettings/ProjectSettings.tsx +++ b/src/components/app/ProjectSettings/ProjectSettings.tsx @@ -25,9 +25,12 @@ import React from 'react'; import Link from 'next/link'; import InviteCodeViewer from '../InviteCodeViewer/InviteCodeViewer'; import ProjectTeamUsers from '../ProjectTeamUsers/ProjectTeamUsers'; +import { useSession } from '@/lib/providers/SessionProvider'; export default function ProjectSettings(project: ProjectWithUsers) { + const { user } = useSession(); const [ghRepo, setGhRepo] = React.useState(''); + const [ghInstallationId, setGhInstallationId] = React.useState(''); const [hasSubmitted, setHasSubmitted] = React.useState(false); const apiUrl = `https://${window.location.hostname}/api/feedback/${project.id}`; React.useEffect(() => { @@ -59,7 +62,9 @@ export default function ProjectSettings(project: ProjectWithUsers) { Project - Github + {user?.id === project.UserProject.find((u) => u.isOwner)?.userId && ( + Github + )} API @@ -161,61 +166,46 @@ export default function ProjectSettings(project: ProjectWithUsers) { - - - - Github Integration - Connect your project to Github - - - { - setGhRepo(`https://github.com/${repo}`); - }} - selected={project.github ? project.github.replace('https://github.com/', '') : ''} - /> -

- Not the results you were expecting? You may have not allowed your user in the{' '} - - installation settings - - . -

- setHasSubmitted(true)} - /> -
-
- - - Issue submission testing - Make sure your setup works! - - - {hasSubmitted ? ( + {user?.id === project.UserProject.find((u) => u.isOwner)?.userId && ( + + + + Github Integration + Connect your project to Github + + + { + setGhRepo(`https://github.com/${repo}`); + setGhInstallationId(id); + }} + selected={project.github ? project.github.replace('https://github.com/', '') : ''} + /> +

+ Not the results you were expecting? You may have not allowed your user in the{' '} + + installation settings + + . +

setHasSubmitted(true)} /> - ) : ( -

- You need to connect your project to a GitHub repository before you can test issue - submission. -

- )} -
-
-
+
+
+ + + Issue submission testing + Make sure your setup works! + + + {hasSubmitted ? ( + + ) : ( +

+ You need to connect your project to a GitHub repository before you can test + issue submission. +

+ )} +
+
+
+ )} @@ -339,4 +353,4 @@ interface ProjectWithUsers extends Project { UserProject: (UserProject & { user: User; })[]; -} \ No newline at end of file +} diff --git a/src/components/app/ProjectTeamUsers/ProjectTeamUsers.tsx b/src/components/app/ProjectTeamUsers/ProjectTeamUsers.tsx index 8c02a00..69d80ce 100644 --- a/src/components/app/ProjectTeamUsers/ProjectTeamUsers.tsx +++ b/src/components/app/ProjectTeamUsers/ProjectTeamUsers.tsx @@ -31,9 +31,10 @@ export default function ProjectTeamUsers(userProject: ProjectTeamUsersProps) { }); }; + // toReversed shows the owner at the top, then at join order return (
    - {users.map((user) => ( + {users.toReversed().map((user) => (
  • {user.user.username.toUpperCase()} - {user.user.username} + {user.user.username}{user.isOwner && ' (owner)'}