Skip to content

Commit 36f2846

Browse files
authored
Creat project page (#144)
* Creat project page * Revire fixes * Lint fixes * Review comments * lint
1 parent d9e8a1a commit 36f2846

File tree

8 files changed

+363
-104
lines changed

8 files changed

+363
-104
lines changed

src/app/(dashboard)/projects/new/page.tsx

Lines changed: 80 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,27 @@
22

33
import { GithubRepoDto } from "@/app/api/github/repo/types/githubRepo.dto";
44
import BreadCrumb from "@/components/breadcrumbs";
5-
import { GithubRepoCard } from "@/components/githubRepoCard";
6-
import ProjectGithubRepositoriesBadge from "@/components/repo/projectGithubRepositoriesBadge";
7-
import { ProjectRepoSearchInput } from "@/components/repo/projectRepoSearchInput";
5+
import ProjectTeamNameForm from "@/components/projects/projectRepoNameForm";
6+
import ProjectRepoSelect from "@/components/projects/projectRepoSelect";
7+
import { TeamCalculationCreated } from "@/components/teams/teamCalculationCreated";
88
import { Button } from "@/components/ui/button";
99
import { Heading } from "@/components/ui/heading";
10-
import { Skeleton } from "@/components/ui/skeleton";
1110
import { Separator } from "@radix-ui/react-separator";
1211
import axios from "axios";
1312
import { useEffect, useState } from "react";
13+
import { toast } from "sonner";
1414

1515
const breadcrumbItems = [
1616
{ title: "Projects", link: "/projects" },
1717
{ title: "New", link: "/projects/new" },
1818
];
1919

2020
export default function CreateNewProjectPage() {
21-
const [isLoading, setIsLoading] = useState<boolean>(true);
22-
const [githubRepos, setGithubRepos] = useState<GithubRepoDto[]>([]);
21+
const [createTeamName, setCreateTeamName] = useState<string>("");
2322
const [selectedGithubRepos, setSelectedGithubRepos] = useState<
2423
GithubRepoDto[]
2524
>([]);
26-
const [page, setPage] = useState<number>(1);
27-
const [canPrevious, setCanPrevious] = useState<boolean>(false);
28-
const [canNext, setCanNext] = useState<boolean>(false);
29-
const [registeredRepositoryNameArray, setRegisteredRepositoryNameArray] =
30-
useState<string[]>([]);
25+
const [currentStep, setCurrentStep] = useState(0);
3126

3227
const handleOnRepoSelect = async (
3328
repo: GithubRepoDto,
@@ -52,27 +47,48 @@ export default function CreateNewProjectPage() {
5247
return repos.filter((repo) => repo.id !== repoIdToRemove);
5348
};
5449

55-
const handleQueryGithubRepos = async (page: number = 1) => {
56-
setIsLoading(true);
57-
const { data } = await axios.get(`/api/github/repo?page=${page}`);
58-
if (data.success && data.gitRepos.length > 0) {
59-
setGithubRepos(data.gitRepos);
50+
const handleCreateTeam = async () => {
51+
nextStep();
52+
const { data } = await axios.post("/api/teams", { name: createTeamName });
53+
if (data.success) {
54+
await handleRegisterRepos(data.createdTeam.id);
55+
handleGenerateReport(data.createdTeam.id);
56+
} else {
57+
previousStep();
6058
}
61-
setIsLoading(false);
6259
};
6360

64-
useEffect(() => {
65-
setCanNext(githubRepos.length === 10);
66-
setCanPrevious(page > 1);
67-
}, [githubRepos, page]);
61+
const handleRegisterRepos = async (projectId: string) => {
62+
await axios.post(`/api/projects/repo`, {
63+
projectId: projectId,
64+
repos: selectedGithubRepos.map((repo) => ({
65+
name: repo.name,
66+
full_name: repo.full_name,
67+
})),
68+
});
69+
};
6870

69-
useEffect(() => {
70-
handleQueryGithubRepos();
71-
}, []);
71+
const handleGenerateReport = async (projectId: string) => {
72+
const { data } = await axios.get(
73+
`/api/github/repo/registered?team_id=${projectId}`,
74+
);
75+
if (data.success && data.registeredRepos.length == 0) {
76+
toast("Failed to start calculation", {
77+
description: "Please select at least one repository to continue",
78+
});
79+
previousStep();
80+
} else {
81+
const { data } = await axios.get(`/api/credmanager?team_id=${projectId}`);
82+
}
83+
};
7284

73-
useEffect(() => {
74-
handleQueryGithubRepos(page);
75-
}, [page]);
85+
const nextStep = () => {
86+
setCurrentStep(currentStep + 1);
87+
};
88+
89+
const previousStep = () => {
90+
setCurrentStep(currentStep - 1);
91+
};
7692

7793
useEffect(() => {
7894
console.log(selectedGithubRepos);
@@ -82,81 +98,48 @@ export default function CreateNewProjectPage() {
8298
<div className="flex-1 space-y-4 p-4 md:p-8 pt-6">
8399
<BreadCrumb items={breadcrumbItems} />
84100
<div className="flex items-start justify-between">
85-
<Heading title={`Add repositories`} description="" />
86-
<div className="flex items-start justify-between w-4/12">
87-
<ProjectRepoSearchInput
88-
onSelectRepo={handleOnRepoSelect}
89-
></ProjectRepoSearchInput>
101+
{currentStep === 0 ? (
102+
<Heading title={`Add repositories`} description="" />
103+
) : currentStep === 1 ? (
104+
<Heading title={`Enter project name`} description="" />
105+
) : (
106+
<Heading title={`Calculating`} description="" />
107+
)}
108+
<div className="flex items-start">
109+
{currentStep == 1 ? (
110+
<Button className="items-center ml-4" onClick={previousStep}>
111+
Previous step
112+
</Button>
113+
) : currentStep === 0 ? (
114+
<Button
115+
className="items-center ml-4"
116+
onClick={nextStep}
117+
disabled={selectedGithubRepos.length == 0}
118+
>
119+
Next Step
120+
</Button>
121+
) : (
122+
<></>
123+
)}
90124
</div>
91125
</div>
92-
<div className="flex items-center py-2 mb-2">
93-
{selectedGithubRepos.map((registeredGitRepo) => (
94-
<ProjectGithubRepositoriesBadge
95-
githubRepoDto={registeredGitRepo}
96-
handleUnregisterRepo={handleOnRepoSelect}
97-
></ProjectGithubRepositoriesBadge>
98-
))}
99-
</div>
100126
<Separator />
101127

102-
{!isLoading ? (
103-
<div>
104-
<div className="grid gap-4 lg:grid-cols-2">
105-
{githubRepos
106-
.filter(
107-
(repo) =>
108-
!registeredRepositoryNameArray.includes(repo.full_name),
109-
)
110-
.map((repo) => {
111-
return (
112-
<GithubRepoCard
113-
githubRepoDto={repo}
114-
onSelectRepo={handleOnRepoSelect}
115-
selected={selectedGithubRepos.includes(repo)}
116-
></GithubRepoCard>
117-
);
118-
})}
119-
</div>
120-
<div className="flex justify-between pt-16">
121-
<div>
122-
<Button
123-
variant="default"
124-
onClick={() => {
125-
setPage(page - 1);
126-
}}
127-
disabled={!canPrevious}
128-
>
129-
Previous Page
130-
</Button>
131-
</div>
132-
<div className="pl-6">
133-
<Button
134-
variant="default"
135-
onClick={() => {
136-
setPage(page + 1);
137-
}}
138-
disabled={!canNext}
139-
>
140-
Next Page
141-
</Button>
142-
</div>
143-
</div>
144-
</div>
128+
{currentStep === 0 ? (
129+
<ProjectRepoSelect
130+
onSelectRepo={handleOnRepoSelect}
131+
selectedGithubRepos={selectedGithubRepos}
132+
></ProjectRepoSelect>
133+
) : currentStep === 1 ? (
134+
<ProjectTeamNameForm
135+
createTeamName={createTeamName}
136+
handleCreateTeam={handleCreateTeam}
137+
setCreateTeamName={setCreateTeamName}
138+
selectedGithubRepos={selectedGithubRepos}
139+
onSelectRepo={handleOnRepoSelect}
140+
></ProjectTeamNameForm>
145141
) : (
146-
<div className="grid gap-4 lg:grid-cols-2">
147-
<div className="flex flex-col space-y-3">
148-
<Skeleton className="h-[165px] w-full rounded-xl" />
149-
</div>
150-
<div className="flex flex-col space-y-3">
151-
<Skeleton className="h-[165px] w-full rounded-xl" />
152-
</div>
153-
<div className="flex flex-col space-y-3">
154-
<Skeleton className="h-[165px] w-full rounded-xl" />
155-
</div>
156-
<div className="flex flex-col space-y-3">
157-
<Skeleton className="h-[165px] w-full rounded-xl" />
158-
</div>
159-
</div>
142+
<TeamCalculationCreated></TeamCalculationCreated>
160143
)}
161144
</div>
162145
);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { RegisterGitRepoDto } from "./repo/route";
2+
3+
export async function registerProjectRepos(
4+
projectId: string,
5+
registerRepos: RegisterGitRepoDto[],
6+
): Promise<void> {
7+
for (const registerRepo of registerRepos) {
8+
const foundRepo = await prisma.githubRepo.findFirst({
9+
where: {
10+
team_id: projectId,
11+
name: registerRepo.name,
12+
full_name: registerRepo.full_name,
13+
},
14+
});
15+
if (!foundRepo) {
16+
await prisma.githubRepo.create({
17+
data: {
18+
team_id: projectId,
19+
name: registerRepo.name,
20+
full_name: registerRepo.full_name,
21+
},
22+
});
23+
}
24+
}
25+
}

src/app/api/projects/repo/route.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { getServerSession } from "next-auth";
2+
import { NextRequest, NextResponse } from "next/server";
3+
import { options } from "../../auth/[...nextauth]/options";
4+
import { registerProjectRepos } from "../projectsService";
5+
6+
export type GitReposRegisterRequest = {
7+
repos: RegisterGitRepoDto[];
8+
projectId: string;
9+
};
10+
11+
export type RegisterGitRepoDto = {
12+
name: string;
13+
full_name: string;
14+
};
15+
16+
export async function POST(req: NextRequest) {
17+
try {
18+
const session = await getServerSession(options);
19+
const registerRepoDto = (await req.json()) as GitReposRegisterRequest;
20+
if (session?.userId && registerRepoDto) {
21+
await registerProjectRepos(
22+
registerRepoDto.projectId,
23+
registerRepoDto.repos,
24+
);
25+
return NextResponse.json({ success: true, created: true });
26+
}
27+
return NextResponse.json({ success: false, created: false });
28+
} catch (error) {
29+
return NextResponse.json({ success: false, created: false });
30+
}
31+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Label } from "@radix-ui/react-label";
2+
import {
3+
Card,
4+
CardContent,
5+
CardDescription,
6+
CardFooter,
7+
CardHeader,
8+
CardTitle,
9+
} from "../ui/card";
10+
import { Button } from "../ui/button";
11+
import { Input } from "../ui/input";
12+
import { GithubRepoDto } from "@/app/api/github/repo/types/githubRepo.dto";
13+
import ProjectGithubRepositoriesBadge from "../repo/projectGithubRepositoriesBadge";
14+
15+
interface ProjectTeamNameFormProps {
16+
createTeamName: string;
17+
handleCreateTeam: () => void;
18+
setCreateTeamName: (teamName: string) => void;
19+
selectedGithubRepos: GithubRepoDto[];
20+
onSelectRepo: (repo: GithubRepoDto, selected?: boolean) => void;
21+
}
22+
23+
export default function ProjectTeamNameForm({
24+
createTeamName,
25+
handleCreateTeam,
26+
setCreateTeamName,
27+
selectedGithubRepos,
28+
onSelectRepo,
29+
}: ProjectTeamNameFormProps) {
30+
return (
31+
<>
32+
<div className="pt-36">
33+
<div className="flex justify-center flex-wrap">
34+
{selectedGithubRepos.map((registeredGitRepo) => (
35+
<div className="pb-2">
36+
<ProjectGithubRepositoriesBadge
37+
githubRepoDto={registeredGitRepo}
38+
handleUnregisterRepo={onSelectRepo}
39+
closeable={false}
40+
></ProjectGithubRepositoriesBadge>
41+
</div>
42+
))}
43+
</div>
44+
45+
<div className="pt-2 flex justify-center">
46+
<Card className="w-[400px]">
47+
<CardHeader>
48+
<CardTitle>Create new project</CardTitle>
49+
<CardDescription>
50+
Analyze contributions with one click.
51+
</CardDescription>
52+
</CardHeader>
53+
<CardContent>
54+
<div className="grid w-full items-center">
55+
<div className="flex flex-col space-y-1.5">
56+
<Label htmlFor="name">Name</Label>
57+
<Input
58+
defaultValue={createTeamName}
59+
onChange={(event) => {
60+
setCreateTeamName(event.target.value);
61+
}}
62+
id="name"
63+
placeholder="Name of your project"
64+
/>
65+
</div>
66+
<div className="flex flex-col space-y-1.5"></div>
67+
</div>
68+
</CardContent>
69+
<CardFooter className="flex justify-between w-full">
70+
<Button
71+
className="w-full"
72+
disabled={
73+
selectedGithubRepos.length == 0 ||
74+
createTeamName.trim().length == 0
75+
}
76+
onClick={handleCreateTeam}
77+
>
78+
Create
79+
</Button>
80+
</CardFooter>
81+
</Card>
82+
</div>
83+
</div>
84+
</>
85+
);
86+
}

0 commit comments

Comments
 (0)