diff --git a/Javascript/LeetCodeStats/index.html b/Javascript/LeetCodeStats/index.html new file mode 100644 index 0000000..a4ef644 --- /dev/null +++ b/Javascript/LeetCodeStats/index.html @@ -0,0 +1,60 @@ + + + + + + LeetMetric + + + +
+ +

LeetMetric

+ +
+

Enter your username below:

+
+ + +
+
+ +
+
+
+
+ +

Easy

+
+
+ +
+
+ +

Medium

+
+
+ +
+
+ +

Hard

+
+
+
+ +
+ +
+
+
+ + + + diff --git a/Javascript/LeetCodeStats/readme.md b/Javascript/LeetCodeStats/readme.md new file mode 100644 index 0000000..bd3f56e --- /dev/null +++ b/Javascript/LeetCodeStats/readme.md @@ -0,0 +1,39 @@ +# 🧩 LeetCode User Stats Tracker + +A simple web app that lets you fetch and visualize **LeetCode user statistics** such as total problems solved, difficulty-wise progress, and overall submissions β€” all displayed in a clean, LeetCode-themed UI. + +--- + +## πŸš€ Features + +- πŸ” Search any LeetCode username +- πŸ“Š Shows total, easy, medium, and hard problems solved +- 🧠 Displays total submission counts +- 🎨 Beautiful LeetCode-inspired UI +- πŸ” Uses LeetCode’s GraphQL API +- 🧰 Built with **HTML, CSS, and Vanilla JavaScript** + +--- + +## βš™οΈ How It Works + +1. Enter a valid **LeetCode username** in the input box. +2. Click **Search**. +3. The app fetches live data from the [LeetCode GraphQL API](https://leetcode.com/graphql/) through a free **CORS proxy**. +4. The data is visualized using animated circular progress bars and submission cards. + +--- + +## πŸ› οΈ Setup & Usage +'''NOTE: +If the code fils to fetch data the go to "https://cors-anywhere.herokuapp.com/corsdemo" and request temperory access here for the code to work. + + +### 1. Clone or Download +```bash +git clone https://github.com/your-username/leetcode-stats-tracker.git +cd leetcode-stats-tracker + + + + diff --git a/Javascript/LeetCodeStats/script.js b/Javascript/LeetCodeStats/script.js new file mode 100644 index 0000000..14b708d --- /dev/null +++ b/Javascript/LeetCodeStats/script.js @@ -0,0 +1,156 @@ +// https://cors-anywhere.herokuapp.com/corsdemo request temperory access here for the code to work + +document.addEventListener("DOMContentLoaded", function () { + const searchButton = document.getElementById("search-btn"); + const usernameInput = document.getElementById("user-input"); + const statsContainer = document.querySelector(".stats-container"); + const easyProgressCircle = document.querySelector(".easy-progress"); + const mediumProgressCircle = document.querySelector(".medium-progress"); + const hardProgressCircle = document.querySelector(".hard-progress"); + const easyLabel = document.getElementById("easy-label"); + const mediumLabel = document.getElementById("medium-label"); + const hardLabel = document.getElementById("hard-label"); + const cardStatsContainer = document.querySelector(".stats-cards"); + + //return true or false based on a regex + function validateUsername(username) { + if (username.trim() === "") { + alert("Username should not be empty"); + return false; + } + const regex = /^[a-zA-Z0-9_-]{1,15}$/; + const isMatching = regex.test(username); + if (!isMatching) { + alert("Invalid Username"); + } + return isMatching; + } + + async function fetchUserDetails(username) { + try { + searchButton.textContent = "Searching..."; + searchButton.disabled = true; + //statsContainer.classList.add("hidden"); + + // const response = await fetch(url); + const proxyUrl = "https://cors-anywhere.herokuapp.com/"; + const targetUrl = "https://leetcode.com/graphql/"; + + const myHeaders = new Headers(); + myHeaders.append("content-type", "application/json"); + + const graphql = JSON.stringify({ + query: + "\n query userSessionProgress($username: String!) {\n allQuestionsCount {\n difficulty\n count\n }\n matchedUser(username: $username) {\n submitStats {\n acSubmissionNum {\n difficulty\n count\n submissions\n }\n totalSubmissionNum {\n difficulty\n count\n submissions\n }\n }\n }\n}\n ", + variables: { username: `${username}` }, + }); + const requestOptions = { + method: "POST", + headers: myHeaders, + body: graphql, + }; + + const response = await fetch(proxyUrl + targetUrl, requestOptions); + if (!response.ok) { + throw new Error("Unable to fetch the User details"); + } + const parsedData = await response.json(); + console.log("Logging data: ", parsedData); + + displayUserData(parsedData); + } catch (error) { + statsContainer.innerHTML = `

${error.message}

`; + } finally { + searchButton.textContent = "Search"; + searchButton.disabled = false; + } + } + + function updateProgress(solved, total, label, circle) { + const progressDegree = (solved / total) * 100; + circle.style.setProperty("--progress-degree", `${progressDegree}%`); + label.textContent = `${solved}/${total}`; + } + + function displayUserData(parsedData) { + const totalQues = parsedData.data.allQuestionsCount[0].count; + const totalEasyQues = parsedData.data.allQuestionsCount[1].count; + const totalMediumQues = parsedData.data.allQuestionsCount[2].count; + const totalHardQues = parsedData.data.allQuestionsCount[3].count; + + const solvedTotalQues = + parsedData.data.matchedUser.submitStats.acSubmissionNum[0].count; + const solvedTotalEasyQues = + parsedData.data.matchedUser.submitStats.acSubmissionNum[1].count; + const solvedTotalMediumQues = + parsedData.data.matchedUser.submitStats.acSubmissionNum[2].count; + const solvedTotalHardQues = + parsedData.data.matchedUser.submitStats.acSubmissionNum[3].count; + + updateProgress( + solvedTotalEasyQues, + totalEasyQues, + easyLabel, + easyProgressCircle + ); + updateProgress( + solvedTotalMediumQues, + totalMediumQues, + mediumLabel, + mediumProgressCircle + ); + updateProgress( + solvedTotalHardQues, + totalHardQues, + hardLabel, + hardProgressCircle + ); + + const cardsData = [ + { + label: "Overall Submissions", + value: + parsedData.data.matchedUser.submitStats.totalSubmissionNum[0] + .submissions, + }, + { + label: "Overall Easy Submissions", + value: + parsedData.data.matchedUser.submitStats.totalSubmissionNum[1] + .submissions, + }, + { + label: "Overall Medium Submissions", + value: + parsedData.data.matchedUser.submitStats.totalSubmissionNum[2] + .submissions, + }, + { + label: "Overall Hard Submissions", + value: + parsedData.data.matchedUser.submitStats.totalSubmissionNum[3] + .submissions, + }, + ]; + + console.log("card ka data: ", cardsData); + + cardStatsContainer.innerHTML = cardsData + .map( + (data) => + `
+

${data.label}

+

${data.value}

+
` + ) + .join(""); + } + + searchButton.addEventListener("click", function () { + const username = usernameInput.value; + console.log("logggin username: ", username); + if (validateUsername(username)) { + fetchUserDetails(username); + } + }); +}); diff --git a/Javascript/LeetCodeStats/style.css b/Javascript/LeetCodeStats/style.css new file mode 100644 index 0000000..668d5d6 --- /dev/null +++ b/Javascript/LeetCodeStats/style.css @@ -0,0 +1,164 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; +} + +body { + background-color: #0f172a; + color: #f1f5f9; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +/* --- Container --- */ +.container { + display: flex; + flex-direction: column; + gap: 1rem; + background-color: #1e293b; + padding: 25px; + border-radius: 12px; + width: 50%; + max-width: 600px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); +} + +/* --- Header --- */ +h1 { + font-size: 2rem; + color: #3a3a02; + text-align: center; + margin-bottom: 15px; +} + +/* --- User Input --- */ +.user-container { + display: flex; + flex-direction: column; + gap: 10px; +} + +.user-input-container { + display: flex; + justify-content: space-between; + gap: 10px; +} + +#user-input { + width: 80%; + padding: 0.5rem; + border-radius: 8px; + border: 1px solid #475569; + background-color: #0f172a; + color: #f1f5f9; + outline: none; +} + +#user-input::placeholder { + color: #94a3b8; +} + +#search-btn { + padding: 0.5rem 1rem; + border-radius: 8px; + border: none; + background-color: #ffff00; + color: #0f172a; + font-weight: bold; + cursor: pointer; + transition: 0.3s; +} + +#search-btn:hover { + background-color: #ffff00; +} + +.circle { + width: 120px; + height: 120px; + border-radius: 50%; + border: 4px solid #334155; + position: relative; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + flex-direction: column; + background: conic-gradient( + var(--progress-color, #22c55e) var(--progress-degree, 0%), + #334155 0% + ); + color: #f1f5f9; +} + +.circle span { + position: relative; + z-index: 2; + font-size: 1.2rem; +} + +.circle p { + font-size: 0.9rem; + margin-top: 5px; + color: #cbd5e1; +} + +.progress { + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 20px; + margin: 1rem 0; +} + +.easy-progress { + --progress-color: #22c55e; +} +.medium-progress { + --progress-color: #ffff00; +} +.hard-progress { + --progress-color: #ef4444; +} + +.stats-cards { + display: flex; + flex-wrap: wrap; + gap: 15px; + justify-content: space-evenly; + margin-top: 1rem; +} + +.card { + background-color: #334155; + color: #f1f5f9; + width: 45%; + max-width: 250px; + padding: 15px; + border-radius: 10px; + display: flex; + flex-direction: column; + align-items: center; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transition: transform 0.3s, background 0.3s; +} + +.card:hover { + transform: translateY(-5px); + background-color: #475569; +} + +.card h4 { + font-size: 1rem; + color: #ffff00; + margin-bottom: 5px; +} + +.card p { + font-size: 1.2rem; + font-weight: bold; +}