diff --git a/.gitignore b/.gitignore index 42c2f0b..1b5152f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. /.env +/devclans-local.pem # dependencies /node_modules /.pnp diff --git a/.vscode/settings.json b/.vscode/settings.json index 7c3cc49..03d2f68 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,23 +1,60 @@ { - "accessibility.alert.format": "never", - "accessibility.alert.save": "never", - "audioCues.volume": 0, - "audioCues.debouncePositionChanges": false, - "audioCues.diffLineDeleted": "off", - "audioCues.diffLineInserted": "off", - "audioCues.terminalQuickFix": "off", - "audioCues.terminalCommandFailed": "off", - "audioCues.taskFailed": "off", - "audioCues.taskCompleted": "off", - "audioCues.onDebugBreak": "off", - "audioCues.notebookCellFailed": "off", - "audioCues.notebookCellCompleted": "off", - "audioCues.noInlayHints": "off", - "audioCues.lineHasInlineSuggestion": "off", - "audioCues.lineHasFoldedArea": "off", - "audioCues.lineHasError": "off", - "audioCues.lineHasBreakpoint": "off", - "audioCues.diffLineModified": "off", - "audioCues.chatResponsePending": "off", - "editor.accessibilitySupport": "off" + "editor.accessibilitySupport": "off", + "accessibility.signals.sounds.volume": 0, + "accessibility.signals.debouncePositionChanges": false, + "accessibility.signals.lineHasError": { + "sound": "off" + }, + "accessibility.signals.lineHasFoldedArea": { + "sound": "off" + }, + "accessibility.signals.lineHasBreakpoint": { + "sound": "off" + }, + "accessibility.signals.lineHasInlineSuggestion": { + "sound": "off" + }, + "accessibility.signals.terminalQuickFix": { + "sound": "off" + }, + "accessibility.signals.onDebugBreak": { + "sound": "off" + }, + "accessibility.signals.noInlayHints": { + "sound": "off" + }, + "accessibility.signals.taskCompleted": { + "sound": "off" + }, + "accessibility.signals.taskFailed": { + "sound": "off" + }, + "accessibility.signals.terminalCommandFailed": { + "sound": "off" + }, + "accessibility.signals.notebookCellCompleted": { + "sound": "off" + }, + "accessibility.signals.notebookCellFailed": { + "sound": "off" + }, + "accessibility.signals.diffLineInserted": { + "sound": "off" + }, + "accessibility.signals.diffLineDeleted": { + "sound": "off" + }, + "accessibility.signals.diffLineModified": { + "sound": "off" + }, + "accessibility.signals.chatResponsePending": { + "sound": "off" + }, + "accessibility.signals.save": { + "announcement": "never" + }, + "accessibility.signals.format": { + "announcement": "never" + }, + "CodeGPT.apiKey": "Mistral" } diff --git a/package.json b/package.json index f243281..2f2e261 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "axios": "^1.6.5", "bson": "^6.2.0", "fast-json-stable-stringify": "^2.1.0", + "fs": "0.0.1-security", "github-calendar": "^2.3.2", "github-calendar-graph": "^0.2.8", "ioredis": "^5.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 776a34d..4516a4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,6 +62,9 @@ dependencies: fast-json-stable-stringify: specifier: ^2.1.0 version: 2.1.0 + fs: + specifier: 0.0.1-security + version: 0.0.1-security github-calendar: specifier: ^2.3.2 version: 2.3.2 @@ -2799,6 +2802,10 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true + /fs@0.0.1-security: + resolution: {integrity: sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==} + dev: false + /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} diff --git a/public/config.github.ts b/public/config.github.ts new file mode 100644 index 0000000..ff46fb9 --- /dev/null +++ b/public/config.github.ts @@ -0,0 +1,79 @@ +import { zodGithubAccessToken } from "@/zod/zod.common"; +import { App, Octokit } from "octokit"; +import fs from 'fs'; + +export const getOctokit = async ({ + installationId, + accessToken, +}: { + installationId?: number; + accessToken?: string; +}) => { + try { + if (installationId) { + const appId = process.env.AUSPY_GITHUB_APP_ID; + + const base64Key = process.env.AUSPY_GITHUB_PRIVATE_KEY; + if (!base64Key) { + throw new Error("Private Key not found"); + } + // Decode the Base64-encoded key to binary data + const binaryKey = Buffer.from(base64Key, "base64"); + // Convert the binary key to a string + const stringKey = binaryKey.toString("utf8"); + + if (!appId || !stringKey) { + throw new Error("App ID or Private Key not found"); + } + const app = new App({ + appId, + privateKey: stringKey, + }); + return { + api: await app.getInstallationOctokit(installationId), + type: "app", + }; + + + // const appId = process.env.AUSPY_GITHUB_APP_ID; + // const filepath = './devclans-local.pem'; + + // const base64Key = fs + // .readFileSync(filepath, "base64") + // .replace("-----BEGIN RSA PRIVATE KEY-----", "") + // .replace("-----END RSA PRIVATE KEY-----", "") + // .trim(); + // // Decode the Base64-encoded key to binary data + // const binaryKey = Buffer.from(base64Key, "base64"); + + // // Convert the binary key to a string (optional) + // const stringKey = binaryKey.toString("utf8"); + // // console.log("My key is: ", myKey); + // if (!appId || !base64Key) { + // throw new Error("App ID or Private Key not found"); + // } + // const app = new App({ + // appId, + // privateKey: stringKey, + // }); + // return { + // api: await app.getInstallationOctokit(installationId), + // type: "app", + // }; + + } else if (zodGithubAccessToken.safeParse(accessToken).success) { + return { + api: new Octokit({ + auth: accessToken, + }), + type: "auth", + }; + } else { + return { api: new Octokit({}), type: "free" }; + } + } catch (error) { + console.error("Error getting octokit", error); + return null; + } +}; + diff --git a/public/copy.png b/public/copy.png new file mode 100644 index 0000000..1190876 Binary files /dev/null and b/public/copy.png differ diff --git a/src/app/api/auth/github/appcallback/route.ts b/src/app/api/auth/github/appcallback/route.ts index e81b0a9..e7b3d63 100644 --- a/src/app/api/auth/github/appcallback/route.ts +++ b/src/app/api/auth/github/appcallback/route.ts @@ -15,7 +15,10 @@ export const dynamic = "force-dynamic"; const handler = async (req: NextRequest) => { try { console.log(" start of github/appcallback"); - // const code = req.nextUrl.searchParams.get("code"); + const code = req.nextUrl.searchParams.get("code"); + const setupAction = req.nextUrl.searchParams.get("setup_action"); + console.log(setupAction, code); + console.log(req.nextUrl.searchParams.get("installation_id")) const installId = zodGithubInstallationId.parse( req.nextUrl.searchParams.get("installation_id") ); @@ -26,6 +29,7 @@ const handler = async (req: NextRequest) => { const userId = zodMongoId.parse(session?.user?._id); // get installed repos const reposData = await getInstalledReposFunc(installId, false); + console.log(reposData) // get user profile await updateData(userId, installId, reposData); console.log("success of github/appcallback, redirecting"); diff --git a/src/app/api/joinTeam/route.ts b/src/app/api/joinTeam/route.ts new file mode 100644 index 0000000..a4cf9a2 --- /dev/null +++ b/src/app/api/joinTeam/route.ts @@ -0,0 +1,44 @@ +// app/api/join-team/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import dbConnect from '@/lib/dbConnect'; +import { ProjectModel } from '@/mongodb/models'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/utils/auth/auth'; +export async function POST(req: NextRequest,res:NextResponse) { + const session: any = await getServerSession(authOptions); + console.log(session); + const userId = session.user._id; + const { teamCode } = await req.json(); + try { + + + await dbConnect(); + + // Find the project with the provided team code + const project = await ProjectModel.findOne({ teamCode }); + + if (!project) { + return NextResponse.json({ error: 'Project not found' }, { status: 404 }); + } + + + console.log(userId); + + // Add the user's ID to the contributors array + if(userId){ + project.team.push(userId); + await project.save(); + + + return NextResponse.json({ message: 'Joined team successfully' }); + } + else{ + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + + + } catch (error) { + console.error('Error joining team:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/leetcode/getUserProfile/[username]/route.ts b/src/app/api/leetcode/getUserProfile/[username]/route.ts new file mode 100644 index 0000000..bf3cba1 --- /dev/null +++ b/src/app/api/leetcode/getUserProfile/[username]/route.ts @@ -0,0 +1,105 @@ +import { NextRequest, NextResponse } from "next/server"; + +async function handler(req:NextRequest, { params }: { params: { username: string } }){ + try{ + const username = params.username; + const query = ` + query getUserProfile($username: String!) { + allQuestionsCount { + difficulty + count + } + matchedUser(username: $username) { + contributions { + points + } + profile { + reputation + ranking + } + submissionCalendar + submitStats { + acSubmissionNum { + difficulty + count + submissions + } + totalSubmissionNum { + difficulty + count + submissions + } + } + } + recentSubmissionList(username: $username) { + title + titleSlug + timestamp + statusDisplay + lang + __typename + } + matchedUserStats: matchedUser(username: $username) { + submitStats: submitStatsGlobal { + acSubmissionNum { + difficulty + count + submissions + __typename + } + totalSubmissionNum { + difficulty + count + submissions + __typename + } + __typename + } + } + } + `; + + const formatData = (data:any) => { + let sendData = { + totalSolved: data.data.matchedUser.submitStats.acSubmissionNum[0].count, + totalSubmissions: data.data.matchedUser.submitStats.totalSubmissionNum, + totalQuestions: data.data.allQuestionsCount[0].count, + easySolved: data.data.matchedUser.submitStats.acSubmissionNum[1].count, + totalEasy: data.data.allQuestionsCount[1].count, + mediumSolved: data.data.matchedUser.submitStats.acSubmissionNum[2].count, + totalMedium: data.data.allQuestionsCount[2].count, + hardSolved: data.data.matchedUser.submitStats.acSubmissionNum[3].count, + totalHard: data.data.allQuestionsCount[3].count, + ranking: data.data.matchedUser.profile.ranking, + contributionPoint: data.data.matchedUser.contributions.points, + reputation: data.data.matchedUser.profile.reputation, + submissionCalendar: JSON.parse(data.data.matchedUser.submissionCalendar), + recentSubmissions: data.data.recentSubmissionList, + matchedUserStats: data.data.matchedUser.submitStats + } + return sendData; + } + const response = await fetch('https://leetcode.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Referer': 'https://leetcode.com', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET', + 'Access-Control-Allow-Headers':'Content-Type, application/json' + }, + body: JSON.stringify({query: query, variables: {username: username}}), + }); + const data = await response.json(); + + return NextResponse.json({ data: formatData(data)}, {status: 200}); + + +} +catch(error){ + console.error('Error getting user profile:', error); + return NextResponse.json({ error: 'Error getting user profile' }, { status: 500 }); +} +} + +export { handler as GET }; \ No newline at end of file diff --git a/src/app/api/project/route.ts b/src/app/api/project/route.ts index 80ba519..3703f5e 100644 --- a/src/app/api/project/route.ts +++ b/src/app/api/project/route.ts @@ -1,6 +1,6 @@ import { getDataQuery } from "@/utils/getDataQuery"; -async function handler(req: Request) { +async function handler(req: Request):Promise{ return getDataQuery(req.url); } diff --git a/src/app/explore/projects/page.tsx b/src/app/explore/projects/page.tsx index 46719ea..4c553af 100644 --- a/src/app/explore/projects/page.tsx +++ b/src/app/explore/projects/page.tsx @@ -18,7 +18,7 @@ const Projects = async ({ params, searchParams }: Partial) => { const projects: ProjectSearchItemProps[] = (await Fetch({ endpoint: "/project" + (str ? `?${str}` : ""), - revalidate: 3600 * 3, // TODO - set revalidate time + revalidate: 1, // TODO - set revalidate time })) || []; console.log( Array.isArray(projects) && projects.length > 0, diff --git a/src/app/join-team/page.tsx b/src/app/join-team/page.tsx new file mode 100644 index 0000000..96ae627 --- /dev/null +++ b/src/app/join-team/page.tsx @@ -0,0 +1,54 @@ +// pages/join-team.tsx +"use client"; +import { ButtonBlue } from '@/components'; +import { useState } from 'react'; + +const JoinTeamPage = () => { + const [teamCode, setTeamCode] = useState(''); + const [message, setMessage] = useState(''); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + try { + const response = await fetch('/api/joinTeam', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ teamCode }), + }); + + const data = await response.json(); + + if (response.ok) { + setMessage(data.message); + } else { + setMessage(data.error); + } + } catch (error) { + console.error('Error joining team :', error); + setMessage('An error occurred while joining the team'); + } + }; + + return ( +
+

Join A Team

+
+ + + + {message &&

{message}

} +
+ ); +}; + +export default JoinTeamPage; \ No newline at end of file diff --git a/src/app/project/[id]/page.tsx b/src/app/project/[id]/page.tsx index 680da51..4df9104 100644 --- a/src/app/project/[id]/page.tsx +++ b/src/app/project/[id]/page.tsx @@ -36,6 +36,7 @@ export async function generateMetadata({ console.error("Project not found in open graph image"); return {}; } + console.log("This us Data:",data) const { title, desc, imgs, skills, repoDetails, domain } = data; const img = Array.isArray(imgs) && imgs.length > 0 diff --git a/src/components/AccountMenu.tsx b/src/components/AccountMenu.tsx index 51076ea..9c480db 100644 --- a/src/components/AccountMenu.tsx +++ b/src/components/AccountMenu.tsx @@ -83,6 +83,10 @@ export default function AccountMenu({ title: "Edit Profile", link: editProfile(userState?._id as string), }, + { + title:"Join Team", + link:"/join-team" + } ]; const handleClose = () => { setAnchorEl(null); diff --git a/src/components/FormNewProject.tsx b/src/components/FormNewProject.tsx index a45bc0c..e5cffff 100644 --- a/src/components/FormNewProject.tsx +++ b/src/components/FormNewProject.tsx @@ -17,6 +17,7 @@ import { usePathname } from "next/navigation"; import { getGHInstaddedRepos } from "@/utils/getInstalledRepos"; import { handleGithubChangeRepos } from "@/utils/handleConnectGithub"; import ButtonConnectGithub from "./buttons/ButtonConnectGithub"; +import { generateTeamCode } from "@/utils/generateTeamCode"; const FormNewProject = ({ defaultValues: dv, @@ -25,6 +26,7 @@ const FormNewProject = ({ defaultValues?: Partial; projectId?: string; }) => { + const [teamCode, setTeamCode] = useState(''); const { data }: any = useSession(); const pathname = usePathname(); const session = data?.user; @@ -40,6 +42,14 @@ const FormNewProject = ({ resolver: zodResolver(zodProjectFormSchema), }); console.log("defaultValues", watch("repoName")); + + const handleGenerateTeamCode = () => { + const code = generateTeamCode(); + console.log("This is the Team code",code); + setTeamCode(code); + setValue('teamCode', code); + }; + const onSubmit: SubmitHandler = async (data) => { try { // const a = zodProjectFormSchema.parse(data); @@ -51,6 +61,10 @@ const FormNewProject = ({ } setDefaultValues(data); console.log("clicked"); + const formData: ProjectFormProps = { + ...data, + teamCode: teamCode || null, + }; // const dt = zodProjectFormSchema.parse(data); // console.log(dt); // return; @@ -59,7 +73,7 @@ const FormNewProject = ({ : "/db/createProject"; const res = await createProjectUser( url, - data, + formData, session, setError, projectId @@ -126,13 +140,16 @@ const FormNewProject = ({ useEffect(() => { updateRepofield(["Loading..."], true); if (!isUserConnected) { + console.log("user not connected"); updateRepofield([], true); return; } if (Array.isArray(repos) && repos.length > 0) { + console.log("user is connected and has some repos"); updateRepofield([null, ...repos], true); } else { if (Array.isArray(repos)) { + console.log("user is connected but has no repos yet"); updateRepofield([], true); } } @@ -189,6 +206,21 @@ const FormNewProject = ({ href={projectId && `/project/${projectId}`} /> )} +
+ + {teamCode && ( +
+

Team Code: {teamCode}

+
+ )} +
} buttonMessage={projectId ? "Update Project" : "Create Project"} @@ -199,4 +231,4 @@ const FormNewProject = ({ ); }; -export default FormNewProject; +export default FormNewProject; \ No newline at end of file diff --git a/src/components/FormNewUser.tsx b/src/components/FormNewUser.tsx index ef1db91..69062d2 100644 --- a/src/components/FormNewUser.tsx +++ b/src/components/FormNewUser.tsx @@ -49,6 +49,7 @@ const FormNewUser = ({ const userid = session?._id; const onSubmit: SubmitHandler = async (data) => { try { + console.log("Clicked update button") data.contactMethodId = selectUserContactId(data) || session?.discordId; // console.log("data", JSON.stringify(data), JSON.stringify(defaultValues)); if (JSON.stringify(data) === JSON.stringify(defaultValues)) { @@ -60,6 +61,7 @@ const FormNewUser = ({ } // console.log("setting data", data); setDefaultValues(data); + console.log("Data from form is ", data); const res = await createProjectUser( `/user/${userid}/update`, data, @@ -103,6 +105,11 @@ const FormNewUser = ({ limit: 10, // min: 3, }, + { + label: "Leetcode:", + name: "leetcode", + desc: "The LeetCode Username of user.", + }, { label: "Skill Level:", name: "skillLevel", diff --git a/src/components/GithubGraph.tsx b/src/components/GithubGraph.tsx index 70108cc..b280a8e 100644 --- a/src/components/GithubGraph.tsx +++ b/src/components/GithubGraph.tsx @@ -2,8 +2,9 @@ import colors from "@/lib/colors"; import { useState } from "react"; import GitHubCalendar from "react-github-calendar"; +import LeetCodeStats from "./LeetCodeStats"; -const GitHubGraph = ({ username }: { username?: string | null }) => { +const GitHubGraph = ({ username, leetcode}: { username?: string | null , leetcode?:string | null }) => { const currentYear = new Date().getFullYear(); const [year, setYear] = useState(currentYear); const removeHashTag = (str: string) => str?.replace("#", "") || "E2E8FF8C"; @@ -45,6 +46,7 @@ const GitHubGraph = ({ username }: { username?: string | null }) => { )}&point=${removeHashTag(colors.text)}&hide_border=true`} alt={`${username}'s github activity graph`} /> + ); }; diff --git a/src/components/LeetCodeStats.tsx b/src/components/LeetCodeStats.tsx new file mode 100644 index 0000000..f08176f --- /dev/null +++ b/src/components/LeetCodeStats.tsx @@ -0,0 +1,70 @@ +import { Fetch } from "@/utils/fetchApi"; +import React from "react"; +import { findLeague } from "@/lib/findLeague"; +function FractionCircle({ fraction, children }:{fraction:number,children:React.ReactNode}) { + // Calculate clip-path values based on fraction + const clipPathBlue = `polygon(0 0, 100% 0, 100% ${fraction * 100}%, 0% ${fraction * 100}%)`; + const clipPathRed = `polygon(0 ${fraction * 100}%, 100% ${fraction * 100}%, 100% 100%, 0% 100%)`; + + return ( +
+
+
+
+
+ {children} +
+
+
+ ); +} + +const LeetCodeStats = async ({username}:{username:string})=>{ + const data = await Fetch({endpoint:`/leetcode/getUserProfile/${username}`}); + console.log(data) + const league = findLeague({ranking:data.data.ranking}); + console.log(league); + + + return ( + +
+

{username} LeetCode Stats

+
+
+
+

Total Problems Solved:

+

{data.data.totalSolved}

+
+
+

Easy Problems Solved:

+

{data.data.easySolved}

+
+
+

Medium Problems Solved:

+

{data.data.mediumSolved}

+
+
+

Hard Problems Solved:

+

{data.data.hardSolved}

+
+
+

Current Ranking:

+

{data.data.ranking}

+
+
+
+ +

{league?.grade}

+
+ +
+ + +
+
+ + ) +} + +export default LeetCodeStats; \ No newline at end of file diff --git a/src/components/project/ExpandDetailsBox.tsx b/src/components/project/ExpandDetailsBox.tsx index 6b7b8ed..15bfcd6 100644 --- a/src/components/project/ExpandDetailsBox.tsx +++ b/src/components/project/ExpandDetailsBox.tsx @@ -24,6 +24,7 @@ const ExpandDetailsBox = ({ }, [data]); return (
+ { setActive(!isActive); diff --git a/src/components/project/ProjectHero.tsx b/src/components/project/ProjectHero.tsx index 2c3a32e..491953c 100644 --- a/src/components/project/ProjectHero.tsx +++ b/src/components/project/ProjectHero.tsx @@ -1,3 +1,4 @@ +"use client" import { ProjectStage, ProjectIconGroup, ChipGroup } from "@/components"; import ProductImg from "@/components/project/ProjectImg"; import { urlProject } from "@/constants"; @@ -9,6 +10,18 @@ import { import { PageProps } from "@/types/page.types"; import ProjectRepoDetails from "./ProjectRepoDetails"; import { convertVideoLinkToEmbed } from "@/utils/ConvertYoutubeLinkToEmbed"; +import { useSession } from "next-auth/react"; +import handleCopy from "@/utils/copyText"; +import Image from "next/image"; +import { useState, useEffect } from "react"; +import { toast } from "react-toastify"; +import { redirect } from "next/dist/server/api-utils"; +interface User { + _id?: string; + name?: string | null; + email?: string | null; + image?: string | null; +} const ProjectHero = ({ skills = ["react", "nextjs", "typescript", "tailwindcss"], @@ -23,15 +36,41 @@ const ProjectHero = ({ repoDetails, video, repoName, + teamCode, + owner, }: Omit & PageProps & { repoDetails: Partial; + }) => { const data = { title, desc, _id, + teamCode, + owner, + }; + const { data: session } = useSession(); + +// _id not found +const userDetails = (session?.user as User)?._id; + +const [isCopied, SetCopied] = useState(false); + +useEffect(() => { + if (isCopied) { + toast.success("Copied to clipboard") + const timer = setTimeout(() => { + SetCopied(false); + }, 100 * 60); + + return () => clearTimeout(timer); // This will clear the timer when the component unmounts + } +}, [isCopied]); + console.log("this is session",session); + console.log("This is going to be the user id", userDetails); + console.log("This is the owner of project:",data.owner?._id); const videoLink = convertVideoLinkToEmbed(video); return (
@@ -47,16 +86,38 @@ const ProjectHero = ({ />
-
-

{data.title}

-

{data.desc}

- {/* chips */} - -
+
+

{data.title}

+

{data.desc}

+ { data.owner?._id === userDetails && + data.teamCode ?( + +
+
+ +

Team Code:

+

{data.teamCode}

+ Copy{handleCopy(data.teamCode); SetCopied(true)}} /> +
+
+ ): + + ( +
+

Generate Team Code

+

You can generate a team code to share with your team members.

+ +
+ ) + + } + {/* chips */} + +
diff --git a/src/components/userPage/UserOverview.tsx b/src/components/userPage/UserOverview.tsx index dcfd48f..0ab99ca 100644 --- a/src/components/userPage/UserOverview.tsx +++ b/src/components/userPage/UserOverview.tsx @@ -14,11 +14,13 @@ const UserOverview = async ({ username, githubDetails, _id, + leetcode, }: { data: ProjectDetailsItemProps[]; username: string; _id: string; githubDetails?: UserProps["githubDetails"]; + leetcode:string; }) => { const session: any = await getServerSessionForServer(); const { readme, login } = githubDetails || {}; @@ -44,7 +46,7 @@ const UserOverview = async ({ href={login as string} /> - + {typeof readme == "string" && typeof readmePurified == "string" && (
{ + if (ranking >= 2500000) { + return { + grade: "C", + fraction: 1 / 7 + } + } + else if(ranking<2500000 && ranking>=1000000){ + return { + grade: "B", + fraction: 2 / 7 + } + + } + else if(ranking<1000000 && ranking>=500000){ + return { + grade: "B+", + fraction: 3 / 7 + } + + } + else if(ranking<500000 && ranking>=100000){ + return { + grade: "A", + fraction: 4 / 7 + } + + } + else if(ranking<100000 && ranking>=10000){ + return { + grade: "A+", + fraction: 5 / 7 + } + + } + else if(ranking<10000 && ranking>=1000){ + return { + grade: "S", + fraction: 6 / 7 + } + + } + else if(ranking<1000){ + return { + grade: "S+", + fraction: 7 / 7 + } + + } + } + + + \ No newline at end of file diff --git a/src/mongodb/models.ts b/src/mongodb/models.ts index f48c5d4..21ba21b 100644 --- a/src/mongodb/models.ts +++ b/src/mongodb/models.ts @@ -20,6 +20,7 @@ const userSchema = new mongoose.Schema( githubDetails: { type: userGithubDetailsSchema, }, + leetcode:{ type:String , unique:true,required:false }, domain: { type: String, enum: projectDomains, @@ -136,6 +137,7 @@ const projectSchema = new mongoose.Schema( topics: [{ type: String, default: [] }], // ml, android skills: [{ type: String, default: [] }], // tech: html, css repoName: { type: String, default: "", unique: true }, + teamCode: { type: String, required: true, unique: true }, likesCount: { type: Number, default: 0 }, bookmarkCount: { type: Number, default: 0 }, projectLinks: [{ type: String, default: [] }], diff --git a/src/mongodb/projectRepoDetails.ts b/src/mongodb/projectRepoDetails.ts index 9146c90..705bfa9 100644 --- a/src/mongodb/projectRepoDetails.ts +++ b/src/mongodb/projectRepoDetails.ts @@ -12,8 +12,7 @@ const projectRepoSchema = new Schema({ default: [], validate: [arrayMaxLengthValidator, "Topics array exceeds maximum length"], }, - commits: { type: Number, required: true, min: 0 }, - lastCommit: { type: Date, required: true }, + lastCommit: { type: Date,default: Date.now }, readme: { type: String, validate: [textLengthValidator, "Readme text exceeds maximum length"], diff --git a/src/types/form.types.tsx b/src/types/form.types.tsx index 8209e42..9698152 100644 --- a/src/types/form.types.tsx +++ b/src/types/form.types.tsx @@ -11,6 +11,7 @@ export type InputFieldProps = { required?: boolean; min?: number; preText?: string; + readOnly?: boolean }; export type FormClientProps = { zodSchema: any; diff --git a/src/types/mongo/project.types.ts b/src/types/mongo/project.types.ts index 6b285cc..b339a86 100644 --- a/src/types/mongo/project.types.ts +++ b/src/types/mongo/project.types.ts @@ -31,6 +31,7 @@ export type ProjectProps = ProjectTeamProps & { owner: mongoose.Types.ObjectId | Partial; contributors: mongoose.Types.ObjectId[] | Partial[]; topics: string[]; + teamCode:string; skills: string[]; repoName: string; likesCount: number; diff --git a/src/types/mongo/user.types.ts b/src/types/mongo/user.types.ts index b8586f7..3f5a338 100644 --- a/src/types/mongo/user.types.ts +++ b/src/types/mongo/user.types.ts @@ -27,6 +27,7 @@ export type UserProps = UserTeamItemProps & linkedin: string; website: string; }; + leetcode:string; ownedProjects: mongoose.Types.ObjectId[]; contributedProjects: mongoose.Types.ObjectId[]; questions: UserQuestionsProps; diff --git a/src/utils/copyText.ts b/src/utils/copyText.ts new file mode 100644 index 0000000..55c47a3 --- /dev/null +++ b/src/utils/copyText.ts @@ -0,0 +1,13 @@ +const handleCopy = async (textToCopy:string) => { + try { + await navigator.clipboard.writeText(textToCopy); + console.log('Text copied to clipboard'); + } catch (err) { + console.error('Failed to copy text: ', err); + } + }; + + + + +export default handleCopy; \ No newline at end of file diff --git a/src/utils/generateTeamCode.ts b/src/utils/generateTeamCode.ts new file mode 100644 index 0000000..117e8e8 --- /dev/null +++ b/src/utils/generateTeamCode.ts @@ -0,0 +1,12 @@ +export const generateTeamCode = (): string => { + const length = 10; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+~`|}{[]:;?><,./-='; + let code = ''; + + for (let i = 0; i < length; i++) { + const randomIndex = Math.floor(Math.random() * characters.length); + code += characters[randomIndex]; + } + + return code; + }; \ No newline at end of file diff --git a/src/utils/getDataQuery.tsx b/src/utils/getDataQuery.tsx index 26e7ed1..7d16064 100644 --- a/src/utils/getDataQuery.tsx +++ b/src/utils/getDataQuery.tsx @@ -17,6 +17,7 @@ export async function getDataQuery( return NextResponse.json({ error: "no request" }); } const isProject = type === "projects"; + console.log("type",isProject); const Enum = isProject ? ProjectRedisKeys : UserRedisKeys; type Props = typeof isProject extends true ? ProjectProps : UserProps; try { diff --git a/src/utils/getInstalledReposFunc.ts b/src/utils/getInstalledReposFunc.ts index 35507b9..36c48a5 100644 --- a/src/utils/getInstalledReposFunc.ts +++ b/src/utils/getInstalledReposFunc.ts @@ -8,7 +8,7 @@ import getServerSessionForServer from "./auth/getServerSessionForApp"; import { decrypt } from "./EncryptFunctions"; export const getInstallationId = async (userId?: string) => { - let installId: number | undefined; + let installId: number ; try { const uid = zodMongoId.parse(userId); console.log("getting install id from cache"); @@ -16,7 +16,7 @@ export const getInstallationId = async (userId?: string) => { installId = parseInt(cacheInstallId || ""); // if not in cache, get installation id from github if (installId) { - console.log("install id from cache hit"); + console.log("install id from cache hit", installId); return installId; } console.log("install id from cache miss"); @@ -27,10 +27,10 @@ export const getInstallationId = async (userId?: string) => { decrypt(id?.githubDetails?.installId?.toString() || "") ); if (installId) { - console.log("install id from db hit"); + console.log("install id from db hit: ", installId); return installId; } - console.log("install id from db miss"); + console.log("install id from db miss: ", installId); return installId; } catch (error) { console.error("error in getInstallationId", error); @@ -61,11 +61,12 @@ export const getInstalledReposFunc = async ( ); console.log("getting octokit"); const api = await getOctokit({ installationId: installId }); + if (!(api && api.type === "app")) { console.log("error getting octokit"); throw new Error("Error getting octokit"); } - console.log("getting repos"); + const repos = await api.api.request("GET /installation/repositories", { headers: { "X-GitHub-Api-Version": "2022-11-28", @@ -99,6 +100,7 @@ export const getInstalledReposFunc = async ( }, }); return reposData; + } catch (error) { console.error("error in getInstalledReposFunc", error); return; diff --git a/src/utils/handleConnectGithub.ts b/src/utils/handleConnectGithub.ts index 66e6685..1c4026d 100644 --- a/src/utils/handleConnectGithub.ts +++ b/src/utils/handleConnectGithub.ts @@ -2,7 +2,7 @@ import { z } from "zod"; export const handleGithubChangeRepos = async () => { - const url = `https://github.com/apps/devclans/installations/new`; + const url = `https://github.com/apps/repogiver/installations/new`; window.open(url, "_blank"); }; diff --git a/src/zod/zod.common.ts b/src/zod/zod.common.ts index 72b41db..c5d61af 100644 --- a/src/zod/zod.common.ts +++ b/src/zod/zod.common.ts @@ -224,6 +224,7 @@ export const zodUserSearchInfoSchema = z.object({ }) .optional() .nullable(), + leetcode: z.string().optional(), githubId: z.string().max(50).optional(), bio: stringSchema.min(10).max(100), username: zodDiscordUsername, @@ -310,6 +311,7 @@ export const zodUserFormSchemaObj = z.object({ .optional(), skills: skillsSchema.min(3), bio: stringSchema.min(10).max(100), + leetcode: z.string().optional(), ...zodUserDataCommonSchema.shape, }); export const zodUserFormSuperRefine = (value: any, context: any) => { @@ -359,6 +361,7 @@ export const zodProjectSearchInfoSchema = z.object({ desc: z.string().min(10).max(200), skills: skillsSchema, team: zodTeamContactSchema.partial().array().optional(), + teamCode:z.string(), skillLevel: z .enum(memberLevels as any) .nullable() @@ -374,7 +377,7 @@ export const zodRepoDetailsSchema = z.object({ watchers: z.number().max(1000000), topics: z.array(z.string()).max(20).default([]).optional(), commits: z.number().max(10000).optional(), - lastCommit: zodDateString, + lastCommit: zodDateString.optional(), created_at: zodDateString.optional(), updated_at: zodDateString.optional(), readme: z.string().max(10000), @@ -475,11 +478,12 @@ export const zodGithubDataSchema = z.object({ owner: projectSchema.shape["owner"], repoName: zodRepoName, }); -export const zodProjectFormSchema = z.object({ +export const zodProjectFormSchema:any = z.object({ title: z.string().trim().min(3).max(50), desc: z.string().min(10).max(200), skills: skillsSchema, team: zodUserTeamItemSchema.optional(), + teamCode: z.string().optional().nullable(), skillLevel: z .enum(memberLevels as any) .nullable() diff --git a/tailwind.config.ts b/tailwind.config.ts index 2418aa9..55cf0a0 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -15,6 +15,8 @@ export const tailwindColors = { cardBg1: "#020D23", green: "#2da519", red: "#D64343", + blueRing:"#d5e5fb", + card:"#081121", }; export default withUt({