From 951f90c25929f73da68d59e36773d314a789f8cd Mon Sep 17 00:00:00 2001
From: "HanuCh@udhary" <137854084+hanuchaudhary@users.noreply.github.com>
Date: Tue, 28 Jan 2025 22:09:33 +0530
Subject: [PATCH 1/3] Edit
---
GQL_Queries/userProfile.ts | 6 +-
app/api/leetcode/userDetails/route.ts | 32 +++++++
app/dashboard/page.tsx | 112 +++++++++++++++++++++++-
next.config.ts | 8 ++
prisma/schema.prisma | 28 +++++-
store/LeetcodeStore/useLeetcodeStore.ts | 77 ++++++++++++++++
utils/leetcode/leetcodeContollers.ts | 13 +++
utils/leetcode/queryLeetCodeAPI.ts | 21 +++++
8 files changed, 288 insertions(+), 9 deletions(-)
create mode 100644 app/api/leetcode/userDetails/route.ts
create mode 100644 store/LeetcodeStore/useLeetcodeStore.ts
create mode 100644 utils/leetcode/leetcodeContollers.ts
create mode 100644 utils/leetcode/queryLeetCodeAPI.ts
diff --git a/GQL_Queries/userProfile.ts b/GQL_Queries/userProfile.ts
index e3ac5d3..3326635 100644
--- a/GQL_Queries/userProfile.ts
+++ b/GQL_Queries/userProfile.ts
@@ -20,7 +20,7 @@
*
* @param {string} $username - The username of the user whose profile is to be fetched.
*/
-const query = `#graphql
+export const userProfileQuery = `#graphql
query getUserProfile($username: String!) {
allQuestionsCount {
difficulty
@@ -87,6 +87,4 @@ query getUserProfile($username: String!) {
statusDisplay
lang
}
-}`;
-
-export default query;
+}`;
\ No newline at end of file
diff --git a/app/api/leetcode/userDetails/route.ts b/app/api/leetcode/userDetails/route.ts
new file mode 100644
index 0000000..0303cba
--- /dev/null
+++ b/app/api/leetcode/userDetails/route.ts
@@ -0,0 +1,32 @@
+import prisma from "@/lib/database/prismaClient";
+import { getLeetCodeUserDetails } from "@/utils/leetcode/leetcodeContollers";
+import { createClient } from "@/utils/supabase/server";
+import { NextResponse } from "next/server";
+
+export async function GET() {
+ try {
+ const supabase = await createClient();
+ const supabaseUser = (await supabase.auth.getUser()).data.user;
+
+ if (!supabaseUser) {
+ return NextResponse.json({ error: "User not authenticated" }, { status: 401 });
+ }
+
+ const user = await prisma.user.findFirst({
+ where: {
+ supabaseId: supabaseUser.id,
+ },
+ });
+
+ if (!user) {
+ return NextResponse.json({ error: "User not found" }, { status: 404 });
+ }
+
+ const LeetCodeUsername = user.leetcodeUsername;
+
+ const result = await getLeetCodeUserDetails(LeetCodeUsername)
+ return NextResponse.json(result);
+ } catch (error: any) {
+ return NextResponse.json({ error: error.message }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index b8a8062..ace6bd2 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -1,9 +1,113 @@
-import React from "react";
+"use client"
+
+import { Badge } from "@/components/ui/badge"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+import { Github, Linkedin, Twitter } from "lucide-react"
+import Image from "next/image"
+import { useLeetcodeStore } from "@/store/LeetcodeStore/useLeetcodeStore"
+import React from "react"
export default function Dashboard() {
+ const { fetchLeetcodeUserProfile, leetcodeUserProfile } = useLeetcodeStore()
+
+ React.useEffect(() => {
+ fetchLeetcodeUserProfile()
+ }, [fetchLeetcodeUserProfile])
+
+ if (!leetcodeUserProfile) {
+ return
Loading...
+ }
+
return (
-
-
This is the dashboard
+
+
+
+ {leetcodeUserProfile.profile.userAvatar && (
+
+
+
+ )}
+
+
{leetcodeUserProfile.profile.realName}
+
@{leetcodeUserProfile.username}
+
+ {leetcodeUserProfile.badges.map((badge) => (
+
+
{badge.displayName}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+ {leetcodeUserProfile.submitStats.acSubmissionNum[0].count || 0}
+
+ Problems Solved
+
+
+ {leetcodeUserProfile.contributions.points}
+ Contribution Points
+
+
+ {leetcodeUserProfile.profile.starRating}
+ Star Rating
+
+
+
+ {leetcodeUserProfile.githubUrl && (
+
+
+
+ )}
+ {leetcodeUserProfile.linkedinUrl && (
+
+
+
+ )}
+ {leetcodeUserProfile.twitterUrl && (
+
+
+
+ )}
+
+
+
+
- );
+ )
}
+
diff --git a/next.config.ts b/next.config.ts
index 02f7357..23be141 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -5,6 +5,14 @@ const nextConfig: NextConfig = {
eslint: {
ignoreDuringBuilds: true, // Ignores all ESLint warnings and errors during builds
},
+ images: {
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: '**', // Match any hostname
+ },
+ ],
+ },
};
export default nextConfig;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index b617548..1797a61 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -27,9 +27,35 @@ model User {
email String @unique
fullName String
gender String
- leetcodeUsername String
+ leetcodeUsername String
password String
isVerified Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
+
+// model leetcodeUser {
+// id String @id @unique @default(uuid())
+// username String
+// githubURL String
+// twitterUrl String
+// linkedinUrl String
+// userAvatar String
+// countryName String
+// skillTags String[]
+// starRating Int
+// badges Badges[]
+// }
+
+// model Badges {
+// id String @id @unique @default(uuid())
+// displayName String
+// icon String
+// creationDate String
+// leetcodeUserId String?
+// leetcodeUser leetcodeUser? @relation(fields: [leetcodeUserId], references: [id])
+// }
+
+
+
+
diff --git a/store/LeetcodeStore/useLeetcodeStore.ts b/store/LeetcodeStore/useLeetcodeStore.ts
new file mode 100644
index 0000000..d616dce
--- /dev/null
+++ b/store/LeetcodeStore/useLeetcodeStore.ts
@@ -0,0 +1,77 @@
+import axios from "axios";
+import { create } from "zustand";
+
+interface leetcodeProfile {
+ username: string;
+ githubUrl: string;
+ twitterUrl: string | null;
+ linkedinUrl: string;
+ contributions: {
+ points: number;
+ questionCount: number;
+ testcaseCount: number;
+ };
+ profile: {
+ realName: string;
+ userAvatar: string;
+ birthday: string | null;
+ ranking: number;
+ reputation: number;
+ websites: string[];
+ countryName: string;
+ company: string | null;
+ school: string | null;
+ skillTags: string[];
+ aboutMe: string;
+ starRating: number;
+ };
+ badges: {
+ id: string;
+ displayName: string;
+ icon: string;
+ creationDate: string;
+ }[];
+ upcomingBadges: {
+ name: string;
+ icon: string;
+ }[];
+ activeBadge: {
+ id: string;
+ displayName: string;
+ icon: string;
+ creationDate: string;
+ };
+ submitStats: {
+ totalSubmissionNum: {
+ difficulty: "All" | "Easy" | "Medium" | "Hard";
+ count: number;
+ submissions: number;
+ }[];
+ acSubmissionNum: {
+ difficulty: "All" | "Easy" | "Medium" | "Hard";
+ count: number;
+ submissions: number;
+ }[];
+ };
+ submissionCalendar: string; // JSON string representing submission data
+ }
+
+
+interface LeetcodeStore {
+ leetcodeUserProfile : leetcodeProfile | null;
+ fetchLeetcodeUserProfile : VoidFunction
+}
+
+export const useLeetcodeStore = create
((set) => ({
+ leetcodeUserProfile: null,
+ fetchLeetcodeUserProfile: async () => {
+ try {
+ const response = await axios.get("/api/leetcode/userDetails");
+ const data = response.data;
+ console.log("data", data);
+ set({ leetcodeUserProfile: data });
+ } catch (error) {
+ console.error(error);
+ }
+ }
+}))
diff --git a/utils/leetcode/leetcodeContollers.ts b/utils/leetcode/leetcodeContollers.ts
new file mode 100644
index 0000000..19d4b74
--- /dev/null
+++ b/utils/leetcode/leetcodeContollers.ts
@@ -0,0 +1,13 @@
+import { userProfileQuery } from "@/GQL_Queries/userProfile";
+import { queryLeetCodeAPI } from "./queryLeetCodeAPI";
+
+export const getLeetCodeUserDetails = async (username: string) => {
+ const response = await queryLeetCodeAPI(userProfileQuery, {
+ username: username,
+ });
+
+ console.log(response);
+ return response.data.matchedUser;
+}
+
+
diff --git a/utils/leetcode/queryLeetCodeAPI.ts b/utils/leetcode/queryLeetCodeAPI.ts
new file mode 100644
index 0000000..7b87e9e
--- /dev/null
+++ b/utils/leetcode/queryLeetCodeAPI.ts
@@ -0,0 +1,21 @@
+import axios from "axios";
+
+const API_URL = process.env.LEETCODE_API_URL || 'https://leetcode.com/graphql';
+
+export async function queryLeetCodeAPI(query: string, variables: any) {
+ try {
+ const response = await axios.post(API_URL, { query, variables });
+ if (response.data.errors) {
+ throw new Error(response.data.errors[0].message);
+ }
+ return response.data;
+ } catch (error: any) {
+ if (error.response) {
+ throw new Error(`Error from LeetCode API: ${error.response.data}`);
+ } else if (error.request) {
+ throw new Error('No response received from LeetCode API');
+ } else {
+ throw new Error(`Error in setting up the request: ${error.message}`);
+ }
+ }
+}
\ No newline at end of file
From 1a02045719d66a6df72e2f8473e8faecab4a0454 Mon Sep 17 00:00:00 2001
From: "HanuCh@udhary" <137854084+hanuchaudhary@users.noreply.github.com>
Date: Tue, 28 Jan 2025 22:15:32 +0530
Subject: [PATCH 2/3] Any
---
app/dashboard/page.tsx | 268 +++++++++++++++++++++++++++--------------
1 file changed, 178 insertions(+), 90 deletions(-)
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index ace6bd2..6df58c7 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -1,113 +1,201 @@
-"use client"
+"use client";
-import { Badge } from "@/components/ui/badge"
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
-import { Github, Linkedin, Twitter } from "lucide-react"
-import Image from "next/image"
-import { useLeetcodeStore } from "@/store/LeetcodeStore/useLeetcodeStore"
-import React from "react"
+import React from "react";
+import Image from "next/image";
+import { useLeetcodeStore } from "@/store/LeetcodeStore/useLeetcodeStore";
+import { Badge } from "@/components/ui/badge";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Skeleton } from "@/components/ui/skeleton";
+import { Github, Linkedin, Twitter, Award, Book, Star } from "lucide-react";
export default function Dashboard() {
- const { fetchLeetcodeUserProfile, leetcodeUserProfile } = useLeetcodeStore()
+ const { fetchLeetcodeUserProfile, leetcodeUserProfile } = useLeetcodeStore();
React.useEffect(() => {
- fetchLeetcodeUserProfile()
- }, [fetchLeetcodeUserProfile])
+ fetchLeetcodeUserProfile();
+ }, [fetchLeetcodeUserProfile]);
if (!leetcodeUserProfile) {
- return Loading...
+ return ;
}
return (
-
-
-
- {leetcodeUserProfile.profile.userAvatar && (
-
-
-
- )}
-
-
{leetcodeUserProfile.profile.realName}
-
@{leetcodeUserProfile.username}
-
- {leetcodeUserProfile.badges.map((badge) => (
-
-
{badge.displayName}
-
-
-
-
+
+
+
+
+
+
+
+
+ {leetcodeUserProfile.profile.realName}
+
+
+ @{leetcodeUserProfile.username}
+
+
+ {leetcodeUserProfile.profile.skillTags.map((skill, index) => (
+
+ {skill}
+
))}
-
-
-
-
-
- {leetcodeUserProfile.submitStats.acSubmissionNum[0].count || 0}
-
- Problems Solved
-
-
- {leetcodeUserProfile.contributions.points}
- Contribution Points
-
-
-
{leetcodeUserProfile.profile.starRating}
-
Star Rating
+
+
+
+
Statistics
+
+ }
+ value={
+ leetcodeUserProfile.submitStats.acSubmissionNum[0].count ||
+ 0
+ }
+ label="Problems Solved"
+ />
+ }
+ value={leetcodeUserProfile.contributions.points}
+ label="Contribution Points"
+ />
+ }
+ value={leetcodeUserProfile.profile.starRating}
+ label="Star Rating"
+ />
+ }
+ value={leetcodeUserProfile.profile.ranking}
+ label="Global Ranking"
+ />
-
- {leetcodeUserProfile.githubUrl && (
-
-
-
- )}
- {leetcodeUserProfile.linkedinUrl && (
-
-
-
- )}
- {leetcodeUserProfile.twitterUrl && (
-
-
-
- )}
+
+
Recent Badges
+
+ {leetcodeUserProfile.badges.slice(0, 6).map((badge) => (
+
+
+
+
+
+ {badge.displayName}
+
+
+ ))}
+
+
+ }
+ />
+ }
+ />
+ }
+ />
+
- )
+ );
+}
+
+function StatItem({
+ icon,
+ value,
+ label,
+}: {
+ icon: React.ReactNode;
+ value: number;
+ label: string;
+}) {
+ return (
+
+ );
}
+function SocialLink({ href, icon }: { href: string; icon: React.ReactNode }) {
+ if (!href) return null;
+ return (
+
+ {icon}
+
+ );
+}
+
+function DashboardSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+
+
+
+
+ {[1, 2].map((section) => (
+
+
+
+ {[1, 2, 3, 4].map((item) => (
+
+ ))}
+
+
+ ))}
+
+
+ {[1, 2, 3].map((icon) => (
+
+ ))}
+
+
+
+
+ );
+}
From d1af0280e58f772d00c82544082276155093d37e Mon Sep 17 00:00:00 2001
From: "HanuCh@udhary" <137854084+hanuchaudhary@users.noreply.github.com>
Date: Tue, 28 Jan 2025 22:18:57 +0530
Subject: [PATCH 3/3] Build fix
---
GQL_Queries/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/GQL_Queries/index.ts b/GQL_Queries/index.ts
index ac32ffe..f138464 100644
--- a/GQL_Queries/index.ts
+++ b/GQL_Queries/index.ts
@@ -6,6 +6,6 @@
*/
export { default as AcSubmissionQuery } from './recentAcSubmit';
export { default as contestQuery } from './contest';
-export { default as userProfileQuery } from './userProfile';
+export { userProfileQuery } from './userProfile';
export { default as submissionQuery } from './recentSubmit';
export { default as languageStatsQuery } from './languageStats';
\ No newline at end of file