diff --git a/package-lock.json b/package-lock.json
index 9f8c976..8a4d1bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,9 +11,14 @@
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.14.2",
+ "@reduxjs/toolkit": "^1.9.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "@types/jest": "^29.5.4",
+ "@types/node": "^20.5.9",
+ "@types/react": "^18.2.21",
+ "@types/react-dom": "^18.2.7",
"axios": "^1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -23,6 +28,7 @@
"redux": "^4.2.1",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.4.2",
+ "typescript": "^5.2.2",
"web-vitals": "^2.1.4"
}
},
@@ -3661,6 +3667,29 @@
"url": "https://opencollective.com/popperjs"
}
},
+ "node_modules/@reduxjs/toolkit": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
+ "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
+ "dependencies": {
+ "immer": "^9.0.21",
+ "redux": "^4.2.1",
+ "redux-thunk": "^2.4.2",
+ "reselect": "^4.1.8"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react-redux": "^7.2.1 || ^8.0.2"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@remix-run/router": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz",
@@ -4483,9 +4512,9 @@
}
},
"node_modules/@types/jest": {
- "version": "29.5.3",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz",
- "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==",
+ "version": "29.5.4",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz",
+ "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==",
"dependencies": {
"expect": "^29.0.0",
"pretty-format": "^29.0.0"
@@ -4735,9 +4764,9 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
},
"node_modules/@types/node": {
- "version": "20.4.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.4.tgz",
- "integrity": "sha512-CukZhumInROvLq3+b5gLev+vgpsIqC2D0deQr/yS1WnxvmYLlJXZpaQrQiseMY+6xusl79E04UjWoqyr+t1/Ew=="
+ "version": "20.6.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz",
+ "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg=="
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@@ -4770,9 +4799,9 @@
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
},
"node_modules/@types/react": {
- "version": "18.2.16",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.16.tgz",
- "integrity": "sha512-LLFWr12ZhBJ4YVw7neWLe6Pk7Ey5R9OCydfuMsz1L8bZxzaawJj2p06Q8/EFEHDeTBQNFLF62X+CG7B2zIyu0Q==",
+ "version": "18.2.21",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz",
+ "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -15391,6 +15420,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
+ "node_modules/reselect": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
+ "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
+ },
"node_modules/resolve": {
"version": "1.22.2",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
@@ -16984,16 +17018,15 @@
}
},
"node_modules/typescript": {
- "version": "4.9.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
- "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
- "peer": true,
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
- "node": ">=4.2.0"
+ "node": ">=14.17"
}
},
"node_modules/unbox-primitive": {
diff --git a/package.json b/package.json
index 71e4df7..55849db 100644
--- a/package.json
+++ b/package.json
@@ -6,9 +6,14 @@
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.14.2",
+ "@reduxjs/toolkit": "^1.9.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "@types/jest": "^29.5.4",
+ "@types/node": "^20.5.9",
+ "@types/react": "^18.2.21",
+ "@types/react-dom": "^18.2.7",
"axios": "^1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -18,6 +23,7 @@
"redux": "^4.2.1",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.4.2",
+ "typescript": "^5.2.2",
"web-vitals": "^2.1.4"
},
"scripts": {
diff --git a/src/components/App.js b/src/components/App.tsx
similarity index 60%
rename from src/components/App.js
rename to src/components/App.tsx
index bb2402c..40bc46a 100644
--- a/src/components/App.js
+++ b/src/components/App.tsx
@@ -1,33 +1,34 @@
-import '../index.css'
-import {createBrowserRouter, RouterProvider} from "react-router-dom";
+import '../index.css';
+import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./home/Home";
import Popular from "./popular/Popular";
import Battle from "./battle/Battle";
import Nav from "./Nav";
import BattleResult from "./battle/BattleResult";
-import {Provider} from "react-redux";
+import { Provider } from "react-redux";
import store from "./redux";
+import React from "react";
const router = createBrowserRouter([
{
path: "/",
- element: ,
+ element: ,
children: [
{
path: "/",
- element: ,
+ element: ,
},
{
path: "/popular",
- element: ,
+ element: ,
},
{
path: "/battle",
- element: ,
+ element: ,
},
{
path: "/battle/result",
- element: ,
+ element: ,
},
{
path: "*",
@@ -37,9 +38,9 @@ const router = createBrowserRouter([
},
]);
-const App = () =>
-
-
-
+const App = (): JSX.Element =>
+
+
+;
export default App;
\ No newline at end of file
diff --git a/src/components/Loading.js b/src/components/Loading.js
deleted file mode 100644
index 28f678b..0000000
--- a/src/components/Loading.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import {Backdrop, CircularProgress} from "@mui/material";
-import {memo} from "react";
-
-const Loading = memo(({isLoading}) => {
- return (
- theme.zIndex.drawer + 1}}
- open={isLoading}>
-
- );
-})
-
-export default Loading;
\ No newline at end of file
diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx
new file mode 100644
index 0000000..a5ae47a
--- /dev/null
+++ b/src/components/Loading.tsx
@@ -0,0 +1,21 @@
+import { Backdrop, CircularProgress } from "@mui/material";
+import React, { memo, ReactNode } from "react";
+
+interface LoadingProps {
+ isLoading: boolean;
+}
+
+const Loading = memo(({ isLoading }: LoadingProps): ReactNode => {
+ return (
+ theme.zIndex.drawer + 1 }}
+ open={isLoading}
+ >
+
+
+ );
+});
+
+export default Loading;
\ No newline at end of file
diff --git a/src/components/Nav.js b/src/components/Nav.tsx
similarity index 76%
rename from src/components/Nav.js
rename to src/components/Nav.tsx
index 59b3b19..8c1a9bc 100644
--- a/src/components/Nav.js
+++ b/src/components/Nav.tsx
@@ -1,7 +1,5 @@
-import {Outlet} from "react-router-dom";
-
-const React = require('react');
-const NavLink = require('react-router-dom').NavLink;
+import { NavLink, Outlet } from 'react-router-dom';
+import React from "react";
const Nav = () => {
return (
diff --git a/src/components/battle/Battle.js b/src/components/battle/Battle.js
deleted file mode 100644
index 84a4dce..0000000
--- a/src/components/battle/Battle.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import {useCallback} from "react";
-import PlayerPreview from "./PlayerPreview";
-import PlayerInput from "./PlayerInput";
-import {Link} from "react-router-dom";
-import {useDispatch, useSelector} from "react-redux";
-import {setPlayerNameAndImage} from "../redux/battle/battle.actions";
-
-const Battle = () => {
- const dispatch = useDispatch();
- const playerOneName = useSelector(state => state.battle.playerOneName)
- const playerTwoName = useSelector(state => state.battle.playerTwoName)
- const playerOneImage = useSelector(state => state.battle.playerOneImage)
- const playerTwoImage = useSelector(state => state.battle.playerTwoImage)
-
- const handleSubmit = useCallback((id, userName) => {
- dispatch(setPlayerNameAndImage({name: userName, id, image: `https://github.com/${userName}.png?size200`}))
- }, [dispatch] );
-
- const handleReset = (id) => {
- dispatch(setPlayerNameAndImage({name: '', id, image: null}))
- }
-
- return (
-
-
- {playerOneImage ?
-
-
- :
-
}
- {playerTwoImage ?
-
-
- :
-
}
-
- {playerOneImage && playerOneImage ?
-
Battle :
- null
- }
-
- );
-}
-
-export default Battle
\ No newline at end of file
diff --git a/src/components/battle/Battle.tsx b/src/components/battle/Battle.tsx
new file mode 100644
index 0000000..bec276a
--- /dev/null
+++ b/src/components/battle/Battle.tsx
@@ -0,0 +1,87 @@
+import React, {useCallback} from "react";
+import PlayerPreview from "./PlayerPreview";
+import PlayerInput from "./PlayerInput";
+import {Link} from "react-router-dom";
+import {useDispatch, useSelector} from "react-redux";
+import {RootState} from "../redux/RootState";
+import {setBattlePlayerNameAndAvatar} from "../redux/battle/battle.slice";
+
+const Battle = (): JSX.Element => {
+ const dispatch = useDispatch();
+ const playerOneName = useSelector((state: RootState) => state.battle.playerOneName);
+ const playerTwoName = useSelector((state: RootState) => state.battle.playerTwoName);
+ const playerOneImage = useSelector((state: RootState) => state.battle.playerOneImage);
+ const playerTwoImage = useSelector((state: RootState) => state.battle.playerTwoImage);
+
+
+ const handleSubmit = useCallback(
+ (id: string, userName: string) => {
+ dispatch(
+ setBattlePlayerNameAndAvatar({
+ name: userName,
+ id,
+ image: `https://github.com/${userName}.png?size200`,
+ })
+ );
+ },
+ [dispatch]
+ );
+
+ const handleReset = (id: string) => {
+ dispatch(setBattlePlayerNameAndAvatar({name: "", id, image: null}));
+ };
+
+ return (
+
+
+ {playerOneImage ? (
+
+
+
+ ) : (
+
+ )}
+ {playerTwoImage ? (
+
+
+
+ ) : (
+
+ )}
+
+ {playerOneImage && playerOneImage ? (
+
+ Battle
+
+ ) : null}
+
+ );
+}
+
+export default Battle;
\ No newline at end of file
diff --git a/src/components/battle/BattleResult.js b/src/components/battle/BattleResult.js
deleted file mode 100644
index 7ab77f5..0000000
--- a/src/components/battle/BattleResult.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import {Fragment, useEffect} from "react";
-import {makeBattle} from "../../utils/api";
-import Loading from "../Loading";
-import PlayerDetails from "./PlayerDetails";
-import {useDispatch, useSelector} from "react-redux";
-import {defineWinnerAndLoser, setBattleResultLoading, setPlayerName} from "../redux/battle/battle.actions";
-import {useLocation} from "react-router-dom";
-
-const BattleResult = () => {
- const location = useLocation();
- const dispatch = useDispatch();
- const playerOneName = useSelector(state => state.battle.playerOneName)
- const playerTwoName = useSelector(state => state.battle.playerTwoName)
- const winner = useSelector(state => state.battle.winner)
- const loser = useSelector(state => state.battle.loser)
- const loading = useSelector(state => state.battle.loadingBattleResult)
-
- useEffect(() => {
- dispatch(setBattleResultLoading(true))
- const searchParams = new URLSearchParams(location.search);
- if (!playerOneName) {
- dispatch(setPlayerName({name: [searchParams.get('playerOneName')], id: 'playerOne'}))
- }
- if (!playerTwoName) {
- dispatch(setPlayerName({name: [searchParams.get('playerTwoName')], id: 'playerTwo'}))
- }
- makeBattle([playerOneName, playerTwoName])
- .then(([winner, loser]) => {
- console.log(winner);
- dispatch(defineWinnerAndLoser({winner: winner, loser: loser}))
- })
- .finally(() => dispatch(setBattleResultLoading(false))
- )
- }, [dispatch, location.search, playerOneName, playerTwoName])
-
- return (
-
-
- {winner && loser ?
-
-
-
- : null
- }
-
- );
-}
-
-export default BattleResult;
\ No newline at end of file
diff --git a/src/components/battle/BattleResult.tsx b/src/components/battle/BattleResult.tsx
new file mode 100644
index 0000000..7bd86f8
--- /dev/null
+++ b/src/components/battle/BattleResult.tsx
@@ -0,0 +1,67 @@
+import React, { Fragment, useEffect } from "react";
+import { makeBattle } from "../../utils/api";
+import Loading from "../Loading";
+import PlayerDetails from "./PlayerDetails";
+import { useDispatch, useSelector } from "react-redux";
+import { useLocation } from "react-router-dom";
+import {RootState} from "../redux/RootState";
+import {defineBattleWinnerAndLoser, setBattlePlayerName, setBattleResultLoading} from "../redux/battle/battle.slice";
+
+const BattleResult = () => {
+ const location = useLocation();
+ const dispatch = useDispatch();
+ const playerOneName = useSelector((state: RootState) => state.battle.playerOneName);
+ const playerTwoName = useSelector((state: RootState) => state.battle.playerTwoName);
+ const winner = useSelector((state: RootState) => state.battle.winner);
+ const loser = useSelector((state: RootState) => state.battle.loser);
+ const loading = useSelector((state: RootState) => state.battle.loadingBattleResult);
+
+ useEffect(() => {
+ dispatch(setBattleResultLoading(true));
+ const searchParams = new URLSearchParams(location.search);
+ if (!playerOneName) {
+ dispatch(
+ setBattlePlayerName({
+ name: searchParams.get("playerOneName"),
+ id: "playerOne",
+ })
+ );
+ }
+ if (!playerTwoName) {
+ dispatch(
+ setBattlePlayerName({
+ name: searchParams.get("playerTwoName"),
+ id: "playerTwo",
+ })
+ );
+ }
+ makeBattle([playerOneName, playerTwoName])
+ .then(([winner, loser]) => {
+ console.log(winner);
+ dispatch(defineBattleWinnerAndLoser({ winner: winner, loser: loser }));
+ })
+ .finally(() => dispatch(setBattleResultLoading(false)));
+ }, [dispatch, location.search, playerOneName, playerTwoName]);
+
+ return (
+
+
+ {winner && loser ? (
+
+
+
+
+ ) : null}
+
+ );
+};
+
+export default BattleResult;
diff --git a/src/components/battle/PlayerDetails.js b/src/components/battle/PlayerDetails.tsx
similarity index 53%
rename from src/components/battle/PlayerDetails.js
rename to src/components/battle/PlayerDetails.tsx
index 6b4c187..c962c97 100644
--- a/src/components/battle/PlayerDetails.js
+++ b/src/components/battle/PlayerDetails.tsx
@@ -1,18 +1,20 @@
import PlayerPreview from "./PlayerPreview";
-import {saveProfileStars} from "../../utils/api";
-import {Button} from "@mui/material";
+import { saveProfileStars } from "../../utils/api";
+import { Button } from "@mui/material";
+import React, {FC} from "react";
-const PlayerDetails = ({label, score, profile}) => {
+const PlayerDetails: FC<{ label: string, score: string, profile: any }> = ({label, score, profile}) => {
return (
-
-
{label}
-
Score : {score}
+
+
{label}
+
Score : {score}
-
-
+ userName={profile.login}
+ >
+
+
- {profile.name}
- {profile.location}
- {profile.company}
@@ -24,8 +26,10 @@ const PlayerDetails = ({label, score, profile}) => {
-
+
);
-}
-export default PlayerDetails;
\ No newline at end of file
+};
+export default PlayerDetails;
diff --git a/src/components/battle/PlayerInput.js b/src/components/battle/PlayerInput.js
deleted file mode 100644
index 8189832..0000000
--- a/src/components/battle/PlayerInput.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import {useDispatch, useSelector} from "react-redux";
-import {setPlayerName} from "../redux/battle/battle.actions";
-
-const PlayerInput = ({id, label, onSubmit}) => {
- const dispatch = useDispatch();
- const userNameOne = useSelector(state => state.battle.playerOneName)
- const userNameTwo = useSelector(state => state.battle.playerTwoName)
-
- const getUserName = () => {
- if (id === 'playerOne') {
- return userNameOne
- }
- if (id === 'playerTwo') {
- return userNameTwo
- }
- return '';
- }
-
- const handleSubmit = (event) => {
- event.preventDefault();
- if (id === 'playerOne') {
- onSubmit(id, userNameOne);
- } else {
- onSubmit(id, userNameTwo);
- }
- }
-
- return (
-
- );
-}
-export default PlayerInput;
\ No newline at end of file
diff --git a/src/components/battle/PlayerInput.tsx b/src/components/battle/PlayerInput.tsx
new file mode 100644
index 0000000..408883a
--- /dev/null
+++ b/src/components/battle/PlayerInput.tsx
@@ -0,0 +1,51 @@
+import { useDispatch, useSelector } from "react-redux";
+import {RootState} from "../redux/RootState";
+import React, {FC} from "react";
+import {setBattlePlayerName} from "../redux/battle/battle.slice";
+
+const PlayerInput: FC<{ id: string, label: string, onSubmit: Function }> = ({ id, label, onSubmit }) => {
+ const dispatch = useDispatch();
+ const userNameOne = useSelector((state: RootState) => state.battle.playerOneName);
+ const userNameTwo = useSelector((state: RootState) => state.battle.playerTwoName);
+
+ const getUserName = () => {
+ if (id === "playerOne") {
+ return userNameOne;
+ }
+ if (id === "playerTwo") {
+ return userNameTwo;
+ }
+ return "";
+ };
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ if (id === "playerOne") {
+ onSubmit(id, userNameOne);
+ } else {
+ onSubmit(id, userNameTwo);
+ }
+ };
+
+ return (
+
+ );
+};
+export default PlayerInput;
diff --git a/src/components/battle/PlayerPreview.js b/src/components/battle/PlayerPreview.js
deleted file mode 100644
index 5104dd4..0000000
--- a/src/components/battle/PlayerPreview.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import {memo} from "react";
-
-const PlayerPreview = memo(({avatar, userName, children}) => {
- return (
-
-
-

-
@{userName}
-
- {children ? children : null}
-
- );
-})
-export default PlayerPreview;
\ No newline at end of file
diff --git a/src/components/battle/PlayerPreview.tsx b/src/components/battle/PlayerPreview.tsx
new file mode 100644
index 0000000..e6bf2be
--- /dev/null
+++ b/src/components/battle/PlayerPreview.tsx
@@ -0,0 +1,15 @@
+import React, {FC, memo} from "react";
+import {PlayerPreviewProps} from "./PlayerPreviewProps";
+
+const PlayerPreview: FC = memo((props):JSX.Element => {
+ return (
+
+
+

+
@{props.userName}
+
+ {props.children ? props.children : null}
+
+ );
+});
+export default PlayerPreview;
diff --git a/src/components/battle/PlayerPreviewProps.ts b/src/components/battle/PlayerPreviewProps.ts
new file mode 100644
index 0000000..cbb5324
--- /dev/null
+++ b/src/components/battle/PlayerPreviewProps.ts
@@ -0,0 +1,6 @@
+export interface PlayerPreviewProps {
+ id: string
+ avatar: string,
+ userName: string,
+ children: any
+}
\ No newline at end of file
diff --git a/src/components/home/Home.js b/src/components/home/Home.js
deleted file mode 100644
index 8fb4714..0000000
--- a/src/components/home/Home.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import LeaderboardTable from "./LeaderboardTable";
-import {Fragment, useEffect} from "react";
-import {useDispatch, useSelector} from "react-redux";
-import {getProfileStarsThunk} from "../redux/home/home.thunks";
-
-const React = require('react');
-
-const Home = () => {
- const dispatch = useDispatch();
- const profiles = useSelector(state => state.home.profiles)
-
- useEffect(() => {
- dispatch(getProfileStarsThunk());
- }, [dispatch])
- return (
-
-
- Leaderboard
-
-
-
- )
-}
-
-export default Home;
\ No newline at end of file
diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx
new file mode 100644
index 0000000..5f4829d
--- /dev/null
+++ b/src/components/home/Home.tsx
@@ -0,0 +1,27 @@
+import LeaderboardTable from "./LeaderboardTable";
+import React, { Fragment, useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {AnyAction} from "redux";
+import {ThunkDispatch} from "redux-thunk";
+import {RootState} from "../redux/RootState";
+import {getProfileStarsThunk} from "../redux/home/home.slice";
+
+const Home = (): JSX.Element => {
+ const dispatch = useDispatch>();
+ const profiles = useSelector((state: RootState) => state.home.profiles);
+
+ useEffect(() => {
+ dispatch(getProfileStarsThunk());
+ }, [dispatch]);
+
+ return (
+
+
+ Leaderboard
+
+
+
+ );
+}
+
+export default Home;
\ No newline at end of file
diff --git a/src/components/home/LeaderboardTable.js b/src/components/home/LeaderboardTable.js
deleted file mode 100644
index 3f53156..0000000
--- a/src/components/home/LeaderboardTable.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import React, {Fragment, memo} from "react";
-import {Table, TableBody, TableCell, TableHead, TableRow} from "@mui/material";
-
-const LeaderboardTable = memo(({profiles}) => {
- if(profiles) {
- return (
-
-
-
- Id
- Name
- Stars
-
-
-
- {
- profiles.map((profile) => {
- return (
-
- {profile.id}
- {profile.profileName}
- {profile.starsCount}
- )
- }
- )}
-
-
- );
- }
- return
-})
-export default LeaderboardTable;
\ No newline at end of file
diff --git a/src/components/home/LeaderboardTable.tsx b/src/components/home/LeaderboardTable.tsx
new file mode 100644
index 0000000..d62ecf6
--- /dev/null
+++ b/src/components/home/LeaderboardTable.tsx
@@ -0,0 +1,42 @@
+import React, { Fragment, memo } from "react";
+import { Table, TableBody, TableCell, TableHead, TableRow } from "@mui/material";
+
+interface Profile {
+ id: number;
+ profileName: string;
+ starsCount: number;
+}
+
+interface LeaderboardTableProps {
+ profiles: Profile[];
+}
+
+const LeaderboardTable = memo(({ profiles }: LeaderboardTableProps) => {
+ if (profiles) {
+ return (
+
+
+
+ Id
+ Name
+ Stars
+
+
+
+ {profiles.map((profile) => {
+ return (
+
+ {profile.id}
+ {profile.profileName}
+ {profile.starsCount}
+
+ );
+ })}
+
+
+ );
+ }
+ return ;
+});
+
+export default LeaderboardTable;
\ No newline at end of file
diff --git a/src/components/popular/LanguageSelector.js b/src/components/popular/LanguageSelector.tsx
similarity index 53%
rename from src/components/popular/LanguageSelector.js
rename to src/components/popular/LanguageSelector.tsx
index a322d8b..f8925e3 100644
--- a/src/components/popular/LanguageSelector.js
+++ b/src/components/popular/LanguageSelector.tsx
@@ -1,9 +1,9 @@
-import {Fragment, memo} from "react";
-import {useDispatch} from "react-redux";
-import {setSelectedLanguage} from "../redux/popular/popular.actions";
+import React, { Fragment, memo } from "react";
+import { useDispatch } from "react-redux";
+import { setSelectedLanguage } from "../redux/popular/popular.slice";
-const languages = ['All', 'Javascript', 'Java', 'Ruby', 'Python', 'CSS'];
-const LanguageSelector = memo( ({selectedLanguageIndex}) => {
+const languages: string[] = ['All', 'Javascript', 'Java', 'Ruby', 'Python', 'CSS'];
+const LanguageSelector = memo(({ selectedLanguageIndex }: { selectedLanguageIndex: number }) => {
const dispatch = useDispatch();
return (
@@ -13,7 +13,7 @@ const LanguageSelector = memo( ({selectedLanguageIndex}) => {
dispatch(setSelectedLanguage(index))}>
{language}
diff --git a/src/components/popular/Popular.js b/src/components/popular/Popular.js
deleted file mode 100644
index 95a003b..0000000
--- a/src/components/popular/Popular.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import {useEffect} from "react";
-import {fetchPopularRepos} from "../../utils/api";
-import Repositories from "./Repositories";
-import Loading from "../Loading";
-import LanguageSelector from "./LanguageSelector";
-import {useDispatch, useSelector} from "react-redux";
-import {setErrors, setRepos, setLoading} from "../redux/popular/popular.actions";
-
-const Popular = () => {
- const dispatch = useDispatch();
-
- const selectedLanguageIndex = useSelector(state => state.popular.selectedLanguageIndex)
- const loading = useSelector(state => state.popular.loading)
- const repos = useSelector(state => state.popular.repos)
- const error = useSelector(state => state.popular.error)
-
- useEffect(() => {
- dispatch(setLoading(true));
- fetchPopularRepos(selectedLanguageIndex)
- .then(repos => dispatch(setRepos(repos)))
- .catch(error => dispatch(setErrors(error)))
- .finally(() => dispatch(setLoading(false)));
- }, [dispatch, selectedLanguageIndex]);
-
- if (error) {
- return {error}
- }
-
- if (loading) {
- return (
-
-
-
-
- )
- } else {
- return (
-
-
-
-
- )
- }
-}
-
-export default Popular
\ No newline at end of file
diff --git a/src/components/popular/Popular.tsx b/src/components/popular/Popular.tsx
new file mode 100644
index 0000000..317ec27
--- /dev/null
+++ b/src/components/popular/Popular.tsx
@@ -0,0 +1,47 @@
+import React, { useEffect } from "react";
+import { fetchPopularRepos } from "../../utils/api";
+import Repositories from "./Repositories";
+import Loading from "../Loading";
+import LanguageSelector from "./LanguageSelector";
+import { useDispatch, useSelector } from "react-redux";
+import { setErrors, setRepos, setLoading } from "../redux/popular/popular.slice";
+import {RootState} from "../redux/RootState";
+
+const Popular = () => {
+ const dispatch = useDispatch();
+
+ const selectedLanguageIndex = useSelector((state: RootState) => state.popular.selectedLanguageIndex);
+ const loading = useSelector((state: RootState) => state.popular.loading);
+ const repos = useSelector((state: RootState) => state.popular.repos);
+ const error = useSelector((state: RootState) => state.popular.error);
+
+ useEffect(() => {
+ dispatch(setLoading(true));
+ fetchPopularRepos(selectedLanguageIndex)
+ .then((repos) => dispatch(setRepos(repos)))
+ .catch((error) => dispatch(setErrors(error)))
+ .finally(() => dispatch(setLoading(false)));
+ }, [dispatch, selectedLanguageIndex]);
+
+ if (error) {
+ return {error}
;
+ }
+
+ if (loading) {
+ return (
+
+
+
+
+ );
+ } else {
+ return (
+
+
+
+
+ );
+ }
+};
+
+export default Popular;
\ No newline at end of file
diff --git a/src/components/popular/Repositories.js b/src/components/popular/Repositories.tsx
similarity index 86%
rename from src/components/popular/Repositories.js
rename to src/components/popular/Repositories.tsx
index 4e63279..6177118 100644
--- a/src/components/popular/Repositories.js
+++ b/src/components/popular/Repositories.tsx
@@ -1,7 +1,9 @@
-const Repositories = ({repos}) => {
+import React from "react";
+
+const Repositories = ({repos}: {repos: Array}) => {
return (
- {repos.map((repo, index) => {
+ {repos.map((repo: any, index: number) => {
return (
-
#{index + 1}
diff --git a/src/components/redux/RootState.ts b/src/components/redux/RootState.ts
new file mode 100644
index 0000000..e1b9d87
--- /dev/null
+++ b/src/components/redux/RootState.ts
@@ -0,0 +1,9 @@
+import PopularState from "./popular/PopularState";
+import BattleState from "./battle/BattleState";
+import Profiles from "./home/Profiles";
+
+export interface RootState {
+ popular: PopularState;
+ battle: BattleState;
+ home: Profiles
+}
diff --git a/src/components/redux/battle/BattleState.ts b/src/components/redux/battle/BattleState.ts
new file mode 100644
index 0000000..fda0f41
--- /dev/null
+++ b/src/components/redux/battle/BattleState.ts
@@ -0,0 +1,9 @@
+export default interface BattleState {
+ playerOneName: string;
+ playerTwoName: string;
+ playerOneImage: string | null;
+ playerTwoImage: string | null;
+ winner: any;
+ loser: any;
+ loadingBattleResult: boolean;
+}
diff --git a/src/components/redux/battle/battle.actions.js b/src/components/redux/battle/battle.actions.js
deleted file mode 100644
index e16da2c..0000000
--- a/src/components/redux/battle/battle.actions.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import {
- BATTLE_DEFINE_WINNER_AND_LOSER, BATTLE_SET_BATTLE_RESULT_LOADING,
- BATTLE_SET_PLAYER_NAME,
- BATTLE_SET_PLAYER_NAME_AND_AVATAR
-} from "./battle.constants";
-
-export const setPlayerName = (payload) => ({
- type: BATTLE_SET_PLAYER_NAME,
- payload
-})
-
-export const setPlayerNameAndImage = (payload) => ({
- type: BATTLE_SET_PLAYER_NAME_AND_AVATAR,
- payload
-})
-
-export const defineWinnerAndLoser = (payload) => ({
- type: BATTLE_DEFINE_WINNER_AND_LOSER,
- payload
-})
-
-export const setBattleResultLoading = (payload) => ({
- type: BATTLE_SET_BATTLE_RESULT_LOADING,
- payload
-})
\ No newline at end of file
diff --git a/src/components/redux/battle/battle.constants.js b/src/components/redux/battle/battle.constants.js
deleted file mode 100644
index 5a55329..0000000
--- a/src/components/redux/battle/battle.constants.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export const BATTLE_SET_PLAYER_NAME = 'BATTLE_SET_PLAYER_NAME';
-export const BATTLE_SET_PLAYER_NAME_AND_AVATAR = 'BATTLE_SET_PLAYER_NAME_AND_AVATAR';
-export const BATTLE_DEFINE_WINNER_AND_LOSER = 'BATTLE_DEFINE_WINNER_AND_LOSER';
-export const BATTLE_SET_BATTLE_RESULT_LOADING = 'BATTLE_SET_BATTLE_RESULT_LOADING';
diff --git a/src/components/redux/battle/battle.reducer.js b/src/components/redux/battle/battle.reducer.js
deleted file mode 100644
index bf377cc..0000000
--- a/src/components/redux/battle/battle.reducer.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import {
- BATTLE_DEFINE_WINNER_AND_LOSER,
- BATTLE_SET_BATTLE_RESULT_LOADING,
- BATTLE_SET_PLAYER_NAME,
- BATTLE_SET_PLAYER_NAME_AND_AVATAR,
-} from "./battle.constants";
-
-const initialState = {
- playerOneName: '',
- playerTwoName: '',
- playerOneImage: null,
- playerTwoImage: null,
- winner: null,
- loser: null,
- loadingBattleResult: true
-}
-
-const battleReducer = (state = initialState, action) => {
- switch (action.type) {
- case BATTLE_SET_PLAYER_NAME: {
- if (action.payload.id === 'playerOne') {
- return {
- ...state,
- playerOneName: action.payload.name
- };
- }
- if (action.payload.id === 'playerTwo') {
- return {
- ...state,
- playerTwoName: action.payload.name
- };
- }
- return state
- }
- case BATTLE_SET_PLAYER_NAME_AND_AVATAR: {
- if (action.payload.id === 'playerOne') {
- return {
- ...state,
- playerOneName: action.payload.name,
- playerOneImage: action.payload.image
- };
- }
- if (action.payload.id === 'playerTwo') {
- return {
- ...state,
- playerTwoName: action.payload.name,
- playerTwoImage: action.payload.image
- };
- }
- return state
- }
- case BATTLE_DEFINE_WINNER_AND_LOSER: {
- return {
- ...state,
- winner: action.payload.winner,
- loser: action.payload.loser,
- loadingBattleResult: false
- };
- }
- case BATTLE_SET_BATTLE_RESULT_LOADING: {
- return {
- ...state,
- loadingBattleResult: action.payload
- };
- }
- default:
- return state
- }
-}
-
-export default battleReducer
\ No newline at end of file
diff --git a/src/components/redux/battle/battle.slice.ts b/src/components/redux/battle/battle.slice.ts
new file mode 100644
index 0000000..29b9ab5
--- /dev/null
+++ b/src/components/redux/battle/battle.slice.ts
@@ -0,0 +1,69 @@
+import { createSlice } from '@reduxjs/toolkit'
+import BattleState from "./BattleState";
+
+const initialState = {
+ playerOneName: '',
+ playerTwoName: '',
+ playerOneImage: '',
+ playerTwoImage: '',
+ winner: null,
+ loser: null,
+ loadingBattleResult: true
+} as BattleState
+
+const battleSlice = createSlice({
+ name: 'battle',
+ initialState,
+ reducers: {
+ setBattlePlayerName(state: BattleState, action: { payload: { id: string, name: string | null}, type: string }) {
+ if (action.payload.id === 'playerOne') {
+ return {
+ ...state,
+ playerOneName: action.payload.name!
+ };
+ }
+ if (action.payload.id === 'playerTwo') {
+ return {
+ ...state,
+ playerTwoName: action.payload.name!
+ };
+ }
+ return state
+ },
+ setBattlePlayerNameAndAvatar(state: BattleState, action: { payload: { id: string, name: string | null , image: string | null}, type: string }) {
+ if (action.payload.id === 'playerOne') {
+ return {
+ ...state,
+ playerOneName: action.payload.name!,
+ playerOneImage: action.payload.image!
+ };
+ }
+ if (action.payload.id === 'playerTwo') {
+ return {
+ ...state,
+ playerTwoName: action.payload.name!,
+ playerTwoImage: action.payload.image!
+ };
+ }
+ return state
+ },
+ defineBattleWinnerAndLoser(state: BattleState, action: { payload: {winner: any, loser: any}, type: string }) {
+ return {
+ ...state,
+ winner: action.payload.winner!,
+ loser: action.payload.loser!,
+ loadingBattleResult: false
+ };
+ },
+ setBattleResultLoading(state: BattleState, action: { payload: boolean, type: string }) {
+ return {
+ ...state,
+ loadingBattleResult: action.payload!
+ };
+ }
+ }
+})
+
+export const { setBattlePlayerName, setBattlePlayerNameAndAvatar, defineBattleWinnerAndLoser, setBattleResultLoading} = battleSlice.actions
+
+export default battleSlice.reducer
\ No newline at end of file
diff --git a/src/components/redux/home/Profiles.ts b/src/components/redux/home/Profiles.ts
new file mode 100644
index 0000000..60740f9
--- /dev/null
+++ b/src/components/redux/home/Profiles.ts
@@ -0,0 +1,3 @@
+export default interface Profiles {
+ profiles: any[];
+}
\ No newline at end of file
diff --git a/src/components/redux/home/home.actions.js b/src/components/redux/home/home.actions.js
deleted file mode 100644
index 8efc743..0000000
--- a/src/components/redux/home/home.actions.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import {
- HOME_GET_PROFILE_STARTS
-} from "./home.constants";
-
-export const getProfileStarts = (payload) => ({
- type: HOME_GET_PROFILE_STARTS,
- payload
-})
diff --git a/src/components/redux/home/home.constants.js b/src/components/redux/home/home.constants.js
deleted file mode 100644
index ad65920..0000000
--- a/src/components/redux/home/home.constants.js
+++ /dev/null
@@ -1 +0,0 @@
-export const HOME_GET_PROFILE_STARTS = 'HOME_GET_PROFILE_STARTS';
\ No newline at end of file
diff --git a/src/components/redux/home/home.reducer.js b/src/components/redux/home/home.reducer.js
deleted file mode 100644
index da7b7cf..0000000
--- a/src/components/redux/home/home.reducer.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import {
- HOME_GET_PROFILE_STARTS,
-} from "./home.constants";
-
-const initialState = {
- profiles: []
-}
-
-const homeReducer = (state = initialState, action) => {
- switch (action.type) {
- case HOME_GET_PROFILE_STARTS: {
- return {
- ...state,
- profiles: action.payload
- };
- }
- default:
- return state
- }
-}
-
-export default homeReducer
\ No newline at end of file
diff --git a/src/components/redux/home/home.slice.ts b/src/components/redux/home/home.slice.ts
new file mode 100644
index 0000000..467cd01
--- /dev/null
+++ b/src/components/redux/home/home.slice.ts
@@ -0,0 +1,29 @@
+import {createAsyncThunk, createSlice} from '@reduxjs/toolkit'
+import Profiles from "./Profiles";
+import {getProfileStars} from "../../../utils/api";
+
+const initialState: Profiles = {
+ profiles: []
+}
+
+export const getProfileStarsThunk = createAsyncThunk('home/getProfileStarsThunk' , async () => {
+ return await getProfileStars()
+})
+
+const homeSlice = createSlice({
+ name: 'home',
+ initialState,
+ reducers: {
+
+ },
+ extraReducers: (builder) => {
+ builder.addCase(getProfileStarsThunk.fulfilled , (state, action) => {
+ return {
+ ...state,
+ profiles: action.payload
+ };
+ })
+ }
+})
+
+export default homeSlice.reducer
\ No newline at end of file
diff --git a/src/components/redux/home/home.thunks.js b/src/components/redux/home/home.thunks.js
deleted file mode 100644
index 13b4668..0000000
--- a/src/components/redux/home/home.thunks.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import {getProfileStars} from "../../../utils/api";
-import {getProfileStarts} from "./home.actions";
-
-export const getProfileStarsThunk = () => {
- return (dispatch) => {
- getProfileStars().then((profiles) => dispatch(getProfileStarts(profiles)))
- }
-}
\ No newline at end of file
diff --git a/src/components/redux/index.js b/src/components/redux/index.js
deleted file mode 100644
index 841cbfd..0000000
--- a/src/components/redux/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import {createStore, applyMiddleware} from "redux";
-import rootReducer from "./root.reducer";
-import thunk from 'redux-thunk'
-
-const store = createStore(rootReducer, applyMiddleware(thunk));
-
-export default store
\ No newline at end of file
diff --git a/src/components/redux/index.ts b/src/components/redux/index.ts
new file mode 100644
index 0000000..0023b24
--- /dev/null
+++ b/src/components/redux/index.ts
@@ -0,0 +1,14 @@
+import {configureStore} from "@reduxjs/toolkit";
+import battleSlice from "./battle/battle.slice";
+import popularSlice from "./popular/popular.slice";
+import homeSlice from "./home/home.slice";
+
+const store = configureStore({
+ reducer: {
+ popular: popularSlice,
+ battle: battleSlice,
+ home: homeSlice
+ }
+})
+
+export default store;
\ No newline at end of file
diff --git a/src/components/redux/popular/PopularState.ts b/src/components/redux/popular/PopularState.ts
new file mode 100644
index 0000000..9ae8d61
--- /dev/null
+++ b/src/components/redux/popular/PopularState.ts
@@ -0,0 +1,6 @@
+export default interface PopularState {
+ selectedLanguageIndex: number;
+ loading: boolean;
+ repos: any[];
+ error: boolean;
+}
diff --git a/src/components/redux/popular/popular.actions.js b/src/components/redux/popular/popular.actions.js
deleted file mode 100644
index 79598e4..0000000
--- a/src/components/redux/popular/popular.actions.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import {
- POPULAR_SET_ERRORS,
- POPULAR_SET_LOADING,
- POPULAR_SET_REPOS,
- POPULAR_SET_SELECTED_LANGUAGE
-} from "./popular.constants";
-
-export const setSelectedLanguage = (payload) => ({
- type: POPULAR_SET_SELECTED_LANGUAGE,
- payload
-})
-
-export const setLoading = (payload) => ({
- type: POPULAR_SET_LOADING,
- payload
-})
-
-export const setRepos = (payload) => ({
- type: POPULAR_SET_REPOS,
- payload
-})
-
-export const setErrors = (payload) => ({
- type: POPULAR_SET_ERRORS,
- payload
-})
\ No newline at end of file
diff --git a/src/components/redux/popular/popular.constants.js b/src/components/redux/popular/popular.constants.js
deleted file mode 100644
index f5e3076..0000000
--- a/src/components/redux/popular/popular.constants.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export const POPULAR_SET_SELECTED_LANGUAGE = 'POPULAR_SET_SELECTED_LANGUAGE';
-export const POPULAR_SET_LOADING = 'POPULAR_SET_LOADING';
-export const POPULAR_SET_REPOS = 'POPULAR_SET_REPOS';
-export const POPULAR_SET_ERRORS = 'POPULAR_SET_ERRORS';
\ No newline at end of file
diff --git a/src/components/redux/popular/popular.reducer.js b/src/components/redux/popular/popular.reducer.js
deleted file mode 100644
index abf315a..0000000
--- a/src/components/redux/popular/popular.reducer.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import {
- POPULAR_SET_ERRORS,
- POPULAR_SET_LOADING,
- POPULAR_SET_REPOS,
- POPULAR_SET_SELECTED_LANGUAGE
-} from "./popular.constants";
-
-const initialState = {
- selectedLanguageIndex: 0,
- loading: false,
- repos: [],
- error: false
-}
-
-const popularReducer = (state = initialState, action) => {
- switch (action.type) {
- case POPULAR_SET_SELECTED_LANGUAGE:
- return {
- ...state,
- selectedLanguageIndex: action.payload
- };
- case POPULAR_SET_LOADING:
- return {
- ...state,
- loading: action.payload
- };
- case POPULAR_SET_REPOS:
- return {
- ...state,
- repos: action.payload
- };
- case POPULAR_SET_ERRORS:
- return {
- ...state,
- errors: action.payload
- };
- default:
- return state;
- }
-}
-
-export default popularReducer
\ No newline at end of file
diff --git a/src/components/redux/popular/popular.slice.ts b/src/components/redux/popular/popular.slice.ts
new file mode 100644
index 0000000..4692332
--- /dev/null
+++ b/src/components/redux/popular/popular.slice.ts
@@ -0,0 +1,44 @@
+import { createSlice } from '@reduxjs/toolkit'
+import PopularState from "./PopularState";
+
+const initialState: PopularState = {
+ selectedLanguageIndex: 0,
+ loading: false,
+ repos: [],
+ error: false
+}
+
+const popularSlice = createSlice({
+ name: 'popular',
+ initialState,
+ reducers: {
+ setSelectedLanguage(state: PopularState, action: { payload: number, type: string }) {
+ return {
+ ...state,
+ selectedLanguageIndex: action.payload
+ };
+ },
+ setLoading(state: PopularState, action: { payload: boolean, type: string }) {
+ return {
+ ...state,
+ loading: action.payload
+ };
+ },
+ setRepos(state: PopularState, action: { payload: [], type: string }) {
+ return {
+ ...state,
+ repos: action.payload
+ };
+ },
+ setErrors(state: PopularState, action: { payload: boolean, type: string }) {
+ return {
+ ...state,
+ errors: action.payload
+ };
+ }
+ }
+})
+
+export const { setSelectedLanguage, setLoading, setRepos, setErrors} = popularSlice.actions
+
+export default popularSlice.reducer
\ No newline at end of file
diff --git a/src/components/redux/root.reducer.js b/src/components/redux/root.reducer.js
deleted file mode 100644
index 657eff7..0000000
--- a/src/components/redux/root.reducer.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import {combineReducers} from "redux";
-import popularReducer from "./popular/popular.reducer";
-import battleReducer from "./battle/battle.reducer";
-import homeReducer from "./home/home.reducer";
-
-export default combineReducers({
- popular: popularReducer,
- battle: battleReducer,
- home: homeReducer
-})
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
deleted file mode 100644
index f1dffeb..0000000
--- a/src/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-import './index.css';
-import App from './components/App';
-import reportWebVitals from './reportWebVitals';
-
-const root = ReactDOM.createRoot(document.getElementById('root'));
-root.render(
-
-
-
-);
-
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();
diff --git a/src/index.tsx b/src/index.tsx
new file mode 100644
index 0000000..b01e862
--- /dev/null
+++ b/src/index.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import './index.css';
+import App from './components/App';
+import { createRoot } from 'react-dom/client';
+
+const element = document.getElementById('root');
+const root = createRoot(element!);
+root.render(
+
+
+
+);
\ No newline at end of file
diff --git a/src/reportWebVitals.js b/src/reportWebVitals.js
deleted file mode 100644
index 5253d3a..0000000
--- a/src/reportWebVitals.js
+++ /dev/null
@@ -1,13 +0,0 @@
-const reportWebVitals = onPerfEntry => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
- getCLS(onPerfEntry);
- getFID(onPerfEntry);
- getFCP(onPerfEntry);
- getLCP(onPerfEntry);
- getTTFB(onPerfEntry);
- });
- }
-};
-
-export default reportWebVitals;
diff --git a/src/utils/api.js b/src/utils/api.ts
similarity index 72%
rename from src/utils/api.js
rename to src/utils/api.ts
index 1870a6a..b69b417 100644
--- a/src/utils/api.js
+++ b/src/utils/api.ts
@@ -1,6 +1,6 @@
import axios from "axios";
-export function fetchPopularRepos (language) {
+export function fetchPopularRepos (language: any) {
const endpoint = window.encodeURI(`https://api.github.com/search/repositories?q=stars:>1+language:${language}&sort=stars&order=desc&type=Repositories`)
return fetch(endpoint)
@@ -14,35 +14,35 @@ export function fetchPopularRepos (language) {
})
}
-export const makeBattle = (players) => {
+export const makeBattle = (players: any) => {
return Promise.all(players.map(getUserData))
.then(sortPlayers)
.catch(handleError);
}
-const handleError = (error) => {
+const handleError = (error: any) => {
console.log(error);
}
-const getProfile = (userName) => {
+const getProfile = (userName: any) => {
return axios.get(`https://api.github.com/users/${userName}`)
.then(user => user.data)
.catch(handleError);
}
-const getRepos = (userName) => {
+const getRepos = (userName: any) => {
return axios.get(`https://api.github.com/users/${userName}/repos?per_page=100`)
.then(repos => repos.data)
.catch(handleError);
}
-const calculateScore = (profile, repos) => {
- const totalStars = repos.reduce((totalStars, repo) => totalStars + repo.stargazers_count, 0);
+const calculateScore = (profile: any, repos: any) => {
+ const totalStars = repos.reduce((totalStars: any, repo: any) => totalStars + repo.stargazers_count, 0);
const followers = profile.followers;
return totalStars + followers;
}
-const getUserData = (userName) => {
+const getUserData = (userName: string) => {
return Promise.all([
getProfile(userName),
getRepos(userName)
@@ -54,7 +54,7 @@ const getUserData = (userName) => {
})
}
-const sortPlayers = (players) => players.sort((a, b) => b.score - a.score);
+const sortPlayers = (players: any) => players.sort((a: any, b: any) => b.score - a.score);
export function getProfileStars() {
return axios.get("https://64edd2531f8721827141d123.mockapi.io/profileStars")
@@ -64,7 +64,7 @@ export function getProfileStars() {
})
}
-export function saveProfileStars (id, userName, stars) {
+export function saveProfileStars (id: any, userName: any, stars: any) {
return axios.post("https://64edd2531f8721827141d123.mockapi.io/profileStars", {id: id, profileName: userName, starsCount: stars})
.then(profiles => profiles.data)
}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..97e2774
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,110 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true, /* Skip type checking all .d.ts files. */
+ "jsx": "react"
+ }
+}