Skip to content

Commit

Permalink
feat(api): add api endpoint and display results in main page
Browse files Browse the repository at this point in the history
  • Loading branch information
cascandaliato committed Nov 3, 2020
1 parent 608663c commit 8e2e5f9
Show file tree
Hide file tree
Showing 17 changed files with 492 additions and 82 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ yarn-error.log*

# vercel
.vercel

.vscode
1 change: 1 addition & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
2 changes: 2 additions & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
3 changes: 3 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
reactStrictMode: true,
};
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@
"start": "next start"
},
"dependencies": {
"@material-ui/core": "^4.11.0",
"axios": "^0.21.0",
"next": "10.0.0",
"react": "17.0.1",
"react-dom": "17.0.1"
"react-dom": "17.0.1",
"rxjs": "^6.6.3"
},
"devDependencies": {
"@types/node": "^14.14.6",
"@types/react": "^16.9.55",
"prettier": "^2.1.2",
"typescript": "^4.0.5"
}
}
7 changes: 0 additions & 7 deletions pages/_app.js

This file was deleted.

8 changes: 8 additions & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "../styles/globals.css";
import { AppProps } from "next/app";

const MyApp = ({ Component, pageProps }: AppProps) => (
<Component {...pageProps} />
);

export default MyApp;
6 changes: 0 additions & 6 deletions pages/api/hello.js

This file was deleted.

92 changes: 92 additions & 0 deletions pages/api/users/[user].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { NextApiRequest, NextApiResponse } from "next";
import axios from "axios";

interface LeetCodeCount {
difficulty: string;
count: number;
}

interface LeetCodeResponse {
data: {
data: {
allQuestionsCount: LeetCodeCount[];
matchedUser: {
profile: { ranking: number };
submitStats: {
acSubmissionNum: LeetCodeCount[];
};
};
};
};
}

interface Output {
error: null | string;
ranking: number | string;
solved: number | string;
solvedOverTotal: string;
solvedPercentage: string;
}

const query = (user: string) =>
`{
"operationName": "getUserProfile",
"variables": { "username" : "${user}" },
"query": "query getUserProfile($username: String!) { allQuestionsCount { difficulty count } matchedUser(username: $username) { profile { starRating ranking } submitStats { acSubmissionNum { difficulty count } } } }"
}`;

const genericErrorMessage =
"error: reach out to github.com/cascandaliato/leetcode-badge";

export default async (req: NextApiRequest, res: NextApiResponse) => {
const {
query: { user },
} = req;

let output: Output;

try {
const {
data: { data },
}: LeetCodeResponse = await axios.post(
"https://leetcode.com/graphql",
query(user as string),
{
headers: {
"content-type": "application/json",
},
}
);

if (!data.matchedUser) throw new Error("User not found");

const ranking = data.matchedUser.profile.ranking;

const solved = data.matchedUser.submitStats.acSubmissionNum.filter(
({ difficulty }) => difficulty === "All"
)[0].count;

const total = data.allQuestionsCount.filter(
({ difficulty }) => difficulty === "All"
)[0].count;

output = {
error: null,
ranking,
solved,
solvedOverTotal: `${solved}/${total}`,
solvedPercentage: `${((solved / total) * 100).toFixed(1)}%`,
};
} catch ({ message }) {
output = {
error: message,
ranking: genericErrorMessage,
solved: genericErrorMessage,
solvedOverTotal: genericErrorMessage,
solvedPercentage: genericErrorMessage,
};
}

res.setHeader("Content-Type", "application/json");
res.status(200).json(output);
};
65 changes: 0 additions & 65 deletions pages/index.js

This file was deleted.

87 changes: 87 additions & 0 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import Head from "next/head";
import { useEffect, useState, FC } from "react";
import { Subject, asyncScheduler } from "rxjs";
import { throttleTime } from "rxjs/operators";
import axios from "axios";

interface LeetCodeStats {
error: null | string;
ranking: number | string;
solved: number | string;
solvedOverTotal: string;
solvedPercentage: string;
}

const defaultStats: LeetCodeStats = {
error: null,
ranking: 0,
solved: 0,
solvedOverTotal: "0/0",
solvedPercentage: "0.0%",
};

const Home: FC = () => {
const [subject] = useState(() => new Subject<string>());
const [stats, setStats] = useState(defaultStats);
const [username, setUsername] = useState("");

const getLeetCodeStats = (u: string) => {
axios
.get(`/api/users/${u}`)
.then(({ data }) => data)
.then(setStats)
.catch(() => setStats(defaultStats));
};

useEffect(() => {
const subscription = subject
.pipe(
throttleTime(400, asyncScheduler, { leading: true, trailing: true })
)
.subscribe(getLeetCodeStats);
return () => subscription.unsubscribe();
}, []);

return (
<main
style={{
height: "100vh",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>

<pre>{JSON.stringify(stats, null, 2)}</pre>
<input
value={username}
onChange={({ target: { value } }) => {
setUsername(value);
subject.next(value);
}}
/>
<object
data="https://img.shields.io/badge/label-message-blue"
type="image/svg+xml"
/>
<img src="https://img.shields.io/badge/label-message-red" />
{/* <footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{" "}
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
</a>
</footer> */}
</main>
);
};

export default Home;
1 change: 1 addition & 0 deletions public/style-flat-square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/style-flat.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/style-for-the-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/style-plastic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
Loading

1 comment on commit 8e2e5f9

@vercel
Copy link

@vercel vercel bot commented on 8e2e5f9 Nov 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.