├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github
├── Capa.png
├── demo.gif
└── logo-full.svg
├── .gitignore
├── @types
└── index.d.ts
├── README.md
├── challenges.json
├── next-env.d.ts
├── next.config.js
├── package.json
├── prettier.config.js
├── public
├── background-logo.png
├── favicon.png
├── icons
│ ├── body.svg
│ ├── close.svg
│ ├── eye.svg
│ ├── level-up.svg
│ ├── level.svg
│ ├── levelup.svg
│ └── twitter.svg
├── logo-full.svg
├── sounds
│ ├── notification.mp3
│ ├── turn-off.mp3
│ └── turn-on.mp3
└── white-logo-full.svg
├── src
├── components
│ ├── ChallengeBox.tsx
│ ├── CompletedChallenges.tsx
│ ├── CountDown.tsx
│ ├── ExperienceBar.tsx
│ ├── Input.tsx
│ ├── LevelUpModal.tsx
│ ├── Loading.tsx
│ ├── Profile.tsx
│ └── SideBar.tsx
├── contexts
│ ├── ChallengesContext.tsx
│ ├── CountDownContext.tsx
│ ├── SessionContext.tsx
│ └── theme.tsx
├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── api
│ │ └── users
│ │ │ ├── all.ts
│ │ │ ├── create.ts
│ │ │ ├── search.ts
│ │ │ └── update.ts
│ ├── home.tsx
│ ├── index.tsx
│ └── leaderboard.tsx
└── styles
│ ├── components
│ ├── ChallengeBox
│ │ └── index.ts
│ ├── CompletedChallenges
│ │ └── index.ts
│ ├── CountDown
│ │ └── index.ts
│ ├── ExperienceBar
│ │ └── index.ts
│ ├── Input
│ │ └── index.ts
│ ├── LevelUpModal
│ │ └── index.ts
│ ├── Loading
│ │ └── index.ts
│ ├── Profile
│ │ └── index.ts
│ └── SideBar
│ │ └── index.ts
│ ├── global.ts
│ ├── pages
│ ├── home.ts
│ ├── index.ts
│ └── leaderboard.ts
│ └── theme.ts
├── tsconfig.json
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["next/babel"],
3 | "plugins": [["styled-components", { "ssr": true }]]
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = false
12 | insert_final_newline = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.js
2 | /*.js
3 | node_modules
4 | build
5 | /src/react-app-env.d.ts
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "extends": [
7 | "plugin:@typescript-eslint/recommended",
8 | "plugin:react/recommended",
9 | "prettier/@typescript-eslint",
10 | "plugin:prettier/recommended",
11 | "airbnb"
12 | ],
13 | "globals": {
14 | "Atomics": "readonly",
15 | "SharedArrayBuffer": "readonly"
16 | },
17 | "parser": "@typescript-eslint/parser",
18 | "parserOptions": {
19 | "ecmaFeatures": {
20 | "jsx": true
21 | },
22 | "ecmaVersion": 2018,
23 | "sourceType": "module"
24 | },
25 | "plugins": [
26 | "prettier",
27 | "react",
28 | "react-hooks",
29 | "@typescript-eslint"
30 | ],
31 | "rules": {
32 | "prettier/prettier": "error",
33 | "react-hooks/rules-of-hooks": "error",
34 | "react-hooks/exhaustive-deps": "warn",
35 | "react/jsx-filename-extension": [1, { "extensions": [".tsx"] }],
36 | "import/prefer-default-export": "off",
37 | "import/extensions": [
38 | "error",
39 | "ignorePackages",
40 | {
41 | "ts": "never",
42 | "tsx": "never"
43 | }
44 | ]
45 | },
46 | "settings": {
47 | "import/resolver": {
48 | "typescript": {}
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/.github/Capa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gabrielpdev/Move-it/61a9ac9ce10ceb23211608c60005c4ba136753a4/.github/Capa.png
--------------------------------------------------------------------------------
/.github/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gabrielpdev/Move-it/61a9ac9ce10ceb23211608c60005c4ba136753a4/.github/demo.gif
--------------------------------------------------------------------------------
/.github/logo-full.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 |
--------------------------------------------------------------------------------
/@types/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.mp3'
2 | declare module '*.svg'
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | ## 📝 Conteúdo
31 |
32 | Sobre |
33 | Iniciando |
34 | Tecnologias Utilizadas |
35 | Como contribuir |
36 |
37 |
38 |
39 | ## 🧐 Sobre
40 |
41 | Move.it é uma Aplicação feita durante a **NWL 4.0** oferecido pela [Rocketseat] :rocket:.
42 | A aplicação visa ajudar pessoas que ficam muito tempo na frente do computador e se esquecem de tempo em tempo
43 | se alongar. Usando do método de Pomodoro, lembrando o usuário a cada período de tempo realizar uma atividade
44 | que ao ser realizada, o usuário ganha experiência, subindo seu level
45 |
46 |
47 |
48 | Demo
49 |
50 | Link
51 |
52 |
53 | ## 🏁 Iniciando
54 |
55 | Instruções de como instalar a aplicação na sua máquina.
56 |
57 | ### ⚒ Instalando
58 |
59 | ```
60 | # 💻 Iniciando
61 |
62 | $ cd web
63 | $ yarn install
64 | $ yarn start
65 |
66 | ```
67 | ## ⛏️ Tecnologias Utilizadas
68 |
69 | As seguintes ferramentas foram usadas na construção do projeto:
70 | - 🌱 [MongoDB][mongodb]
71 | - 🔵 [TypeScript][typescript]
72 | - ⚛️ [React][reactjs]
73 | - 🔼 [NextJs][next]
74 | - ⏺ [Framer Motion][framermotion]
75 | - 🍪 [JavaScript Cookie][jscookie]
76 | - 💅 [Styled-components][styled-components]
77 |
78 | ## 🤔 Como contribuir
79 |
80 | - Faça um fork desse repositório;
81 | - Cria uma branch com a sua feature: `git checkout -b minha-feature`;
82 | - Faça commit das suas alterações: `git commit -m 'feat: Minha nova feature'`;
83 | - Faça push para a sua branch: `git push origin minha-feature`.
84 |
85 | Feito com ❤️ por Gabriel Pereira 👋🏽 [Entre em contato!](https://www.linkedin.com/in/gabriel-pereira-oliveira-78b1801ab/)
86 |
87 | [jscookie]: https://github.com/js-cookie/js-cookie
88 | [mongodb]: https://www.mongodb.com
89 | [framermotion]: https://www.framer.com/motion/
90 | [next]: https://nextjs.org/
91 | [typescript]: https://www.typescriptlang.org/
92 | [reactjs]: https://reactjs.org
93 | [Rocketseat]:https://github.com/Rocketseat
94 | [styled-components]:https://styled-components.com/
95 |
96 |
--------------------------------------------------------------------------------
/challenges.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "body",
4 | "description": "Estique um de seus braços com a palma da mão virada para frente e puxe os dedos para cima por 10 segundos por mão.",
5 | "amount": 80
6 | },
7 | {
8 | "type": "body",
9 | "description": "Estique seu braço contra o peito e puxe-o utilizando o outro braço por 10 segundos por braço.",
10 | "amount": 60
11 | },
12 | {
13 | "type": "body",
14 | "description": "Puxe seu pescoço com a ajuda da mão para a direita e para a esquerda, permanecendo na posição por alguns segundos.",
15 | "amount": 70
16 | },
17 | {
18 | "type": "body",
19 | "description": "Com as duas mãos na parte de trás da cabeça, leve-a para baixo, alongando a parte de trás da região.",
20 | "amount": 60
21 | },
22 | {
23 | "type": "body",
24 | "description": "Cruze as pernas e desça com as mãos esticadas em direção ao chão. Repita o movimento com a outra perna na frente.",
25 | "amount": 100
26 | },
27 | {
28 | "type": "body",
29 | "description": "Sentado, abra as pernas e tente encostar as palmas das mãos no chão, repita 3 vezes por 5 segundos.",
30 | "amount": 80
31 | },
32 | {
33 | "type": "body",
34 | "description": "Puxe o joelho de encontro ao peito e segure, troque de perna após 10 segundos.",
35 | "amount": 50
36 | },
37 | {
38 | "type": "body",
39 | "description": "Sentado, cruze uma perna e incline seu tronco à frente, troque de perna após 10 segundos.",
40 | "amount": 80
41 | },
42 | {
43 | "type": "eye",
44 | "description": "Sentado, feche os olhos e cubra-os com as palmas da mão durante 2 minutos, depois abra normalmente.",
45 | "amount": 90
46 | },
47 | {
48 | "type": "eye",
49 | "description": "Em algum ambiente aberto, olhe o mais longe que puder em quatro direções por 3s, mexa apenas os olhos. Repita 3 vezes.",
50 | "amount": 140
51 | },
52 | {
53 | "type": "eye",
54 | "description": "Usando os polegares, massage a área abaixo das sobrancelhas em movimentos circulares por 15 segundos.",
55 | "amount": 70
56 | },
57 | {
58 | "type": "body",
59 | "description": "Em pé, gire a cintura o máximo que puder para a esquerda, segure por cinco segundos. Repita para a direita.",
60 | "amount": 90
61 | }
62 | ]
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | webpack: (config) => {
3 | config.module.rules.push({
4 | test: /\.(mp3)$/,
5 | use: {
6 | loader: 'file-loader',
7 | options: {
8 | publicPath: '/_next/static/sounds/',
9 | outputPath: 'static/sounds/',
10 | name: '[name].[ext]',
11 | esModule: false
12 | }
13 | }
14 | })
15 | return config
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-styled-components",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "dev": "next",
6 | "build": "next build",
7 | "start": "next start"
8 | },
9 | "dependencies": {
10 | "@vercel/node": "^1.9.0",
11 | "axios": "^0.21.1",
12 | "framer-motion": "^3.7.0",
13 | "js-cookie": "^2.2.1",
14 | "mongodb": "^3.6.4",
15 | "next": "latest",
16 | "react": "^16.8.0",
17 | "react-dom": "^16.8.0",
18 | "react-icons": "^4.2.0",
19 | "react-is": "^16.8.0",
20 | "styled-components": "^5.0.0",
21 | "use-sound": "^2.0.1"
22 | },
23 | "devDependencies": {
24 | "@types/js-cookie": "^2.2.6",
25 | "@types/mongodb": "^3.6.8",
26 | "@types/node": "^14.14.31",
27 | "@types/react": "^17.0.2",
28 | "@typescript-eslint/eslint-plugin": "^4.15.2",
29 | "@typescript-eslint/parser": "^4.15.2",
30 | "babel-plugin-styled-components": "^1.8.0",
31 | "eslint": "^5.16.0",
32 | "eslint-config-airbnb": "^18.2.1",
33 | "eslint-config-prettier": "^8.0.0",
34 | "eslint-import-resolver-typescript": "^2.4.0",
35 | "eslint-plugin-import": "^2.22.1",
36 | "eslint-plugin-jsx-a11y": "^6.4.1",
37 | "eslint-plugin-prettier": "^3.3.1",
38 | "eslint-plugin-react": "^7.21.5",
39 | "eslint-plugin-react-hooks": "^4",
40 | "file-loader": "^6.2.0",
41 | "prettier": "^2.2.1",
42 | "typescript": "^4.1.5"
43 | },
44 | "license": "MIT"
45 | }
46 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | trailingComma: 'all',
4 | arrowParens: 'avoid',
5 | }
--------------------------------------------------------------------------------
/public/background-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gabrielpdev/Move-it/61a9ac9ce10ceb23211608c60005c4ba136753a4/public/background-logo.png
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gabrielpdev/Move-it/61a9ac9ce10ceb23211608c60005c4ba136753a4/public/favicon.png
--------------------------------------------------------------------------------
/public/icons/body.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/icons/eye.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/public/icons/level-up.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/public/icons/level.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/public/icons/levelup.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/public/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/logo-full.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/public/sounds/notification.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gabrielpdev/Move-it/61a9ac9ce10ceb23211608c60005c4ba136753a4/public/sounds/notification.mp3
--------------------------------------------------------------------------------
/public/sounds/turn-off.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gabrielpdev/Move-it/61a9ac9ce10ceb23211608c60005c4ba136753a4/public/sounds/turn-off.mp3
--------------------------------------------------------------------------------
/public/sounds/turn-on.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gabrielpdev/Move-it/61a9ac9ce10ceb23211608c60005c4ba136753a4/public/sounds/turn-on.mp3
--------------------------------------------------------------------------------
/public/white-logo-full.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/ChallengeBox.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useProvider } from '../contexts/ChallengesContext';
3 | import { useCountDown } from '../contexts/CountDownContext';
4 |
5 | import { Container, ChallengeActive, ChallengeNotActive,FailedButton, SucceededButton } from '../styles/components/ChallengeBox'
6 |
7 | export const ChallengeBox: React.FC = () => {
8 | const { activeChallenge, resetChallenge, completeChallenge } = useProvider();
9 | const { resetCountDown } = useCountDown();
10 |
11 | function handleChallengeSucceeded(){
12 | completeChallenge()
13 | resetCountDown()
14 | }
15 |
16 | function handleChallengeFailed(){
17 | resetChallenge()
18 | resetCountDown()
19 | }
20 |
21 | return (
22 |
23 | {activeChallenge ? (
24 |
25 | Ganhe {activeChallenge.amount} xp
26 |
27 |
28 |
29 | Novo desafio
30 | {activeChallenge.description}
31 |
32 |
33 |
34 | Falhei
35 | Completei
36 |
37 |
38 | ) : (
39 |
40 | Finalize um ciclo para receber um desafio
41 |
42 |
43 |
44 | Avance de level completando desafios.
45 |
46 |
47 | )}
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/CompletedChallenges.tsx:
--------------------------------------------------------------------------------
1 | import { useProvider } from '../contexts/ChallengesContext'
2 | import { Container } from '../styles/components/CompletedChallenges'
3 |
4 | export const CompletedChallenges: React.FC = () => {
5 |
6 | const { challengesCompleted } = useProvider();
7 |
8 | return (
9 |
10 | Desafios Completos
11 | {challengesCompleted}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/CountDown.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { useProvider } from '../contexts/ChallengesContext';
3 | import { useCountDown } from '../contexts/CountDownContext';
4 |
5 | import { CountDownContainer, CountDownButton, StopCountDownButton, FinishedCountDownButton } from '../styles/components/CountDown'
6 |
7 |
8 | export const CountDown: React.FC = () => {
9 | const {
10 | hasFinished,
11 | isActive,
12 | minutes,
13 | resetCountDown,
14 | seconds,
15 | startCountDown
16 | } = useCountDown();
17 |
18 | const [minuteLeft, minuteRight] = String(minutes).padStart(2, '0').split('');
19 | const [secondLeft, secondRight] = String(seconds).padStart(2, '0').split('');
20 |
21 |
22 |
23 | return (
24 |
25 |
26 |
27 | {minuteLeft}
28 | {minuteRight}
29 |
30 |
31 | :
32 |
33 |
34 | {secondLeft}
35 | {secondRight}
36 |
37 |
38 |
39 | {hasFinished ? (
40 |
41 | Ciclo encerrado
42 |
43 | ) : (
44 | <>
45 | {!isActive ? (
46 |
47 | Iniciar um ciclo
48 |
49 | ) : (
50 |
51 | Abandonar o ciclo
52 |
53 | )}
54 | >
55 | )}
56 |
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/ExperienceBar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { motion } from "framer-motion"
3 |
4 | import { useProvider } from '../contexts/ChallengesContext'
5 |
6 | import { Container } from '../styles/components/ExperienceBar'
7 |
8 | export const ExperienceBar: React.FC = () => {
9 | const { currentExperience, experienceToNextLevel } = useProvider();
10 |
11 | const percentToNextLevel = Math.round(currentExperience * 100) / experienceToNextLevel;
12 |
13 | return (
14 |
15 | 0 xp
16 |
17 |
20 |
21 | {currentExperience} xp
22 |
23 | {experienceToNextLevel} xp
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/Input.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | InputHTMLAttributes,
3 | useRef,
4 | useState,
5 | useCallback,
6 | } from 'react';
7 |
8 | import { FiArrowRight, FiLoader } from 'react-icons/fi';
9 | import { Container } from '../styles/components/Input';
10 |
11 | const Input: React.FC> = ({
12 | ...rest
13 | }) => {
14 | const inputRef = useRef(null);
15 |
16 | const [isLoading, setIsLoading] = useState(false);
17 | const [isFocused, setIsFocused] = useState(false);
18 | const [isFilled, setIsFilled] = useState(false);
19 |
20 | const handleInputFocus = useCallback(() => {
21 | setIsFocused(true);
22 | }, []);
23 |
24 | const handleInputBlur = useCallback(() => {
25 | setIsFocused(false);
26 |
27 | setIsFilled(!!inputRef.current?.value);
28 | }, []);
29 |
30 | return (
31 |
36 |
42 | {isFilled && setIsLoading(true)}}>
43 | {isLoading ? : }
44 |
45 |
46 | );
47 | };
48 |
49 | export default Input;
--------------------------------------------------------------------------------
/src/components/LevelUpModal.tsx:
--------------------------------------------------------------------------------
1 | import { motion } from "framer-motion"
2 |
3 | import { useProvider } from '../contexts/ChallengesContext';
4 | import { Overlay, Container } from '../styles/components/LevelUpModal'
5 |
6 | export default function LevelUpModal() {
7 | const { level, closeLevelUpModal } = useProvider();
8 |
9 | return (
10 |
11 |
12 |
13 |
14 | Parabéns
15 | Você alcançou um novo level.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Loading.tsx:
--------------------------------------------------------------------------------
1 | import { Container } from '../styles/components/Loading'
2 |
3 | export function Loading() {
4 | const moch = [1,2,3,4,5,6]
5 |
6 | return (
7 |
8 |
9 |
10 |
11 | POSIÇÃO
12 | USUÁRIO
13 | DESAFIOS
14 | EXPERIÊNCIA ATUAl
15 |
16 |
17 |
18 |
19 | {moch.map((item, index) => (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ))}
37 |
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/Profile.tsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router'
2 | import { Container } from '../styles/components/Profile'
3 | import { FiLogOut } from "react-icons/fi";
4 | import { useSession } from '../contexts/SessionContext';
5 | import { useProvider } from '../contexts/ChallengesContext';
6 |
7 | interface IUserGithub {
8 | name: string
9 | level: number
10 | avatar_url: string
11 | }
12 |
13 | export default function Profile(props: IUserGithub) {
14 | const { singOut } = useSession();
15 | const { level } = useProvider();
16 |
17 | async function handleSignout(){
18 | await singOut()
19 | }
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
{props?.name}
27 |
28 |
29 | Level {level}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/SideBar.tsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import React, { useEffect } from 'react'
3 | import useSound from 'use-sound'
4 | import Link from 'next/link';
5 |
6 | import { FiSun, FiMoon, FiHome, FiAward } from "react-icons/fi";
7 |
8 | import { useTheme } from '../contexts/theme';
9 | import { Container } from '../styles/components/SideBar'
10 |
11 | import turnOnSound from '../../public/sounds/turn-on.mp3';
12 | import turnOffSound from '../../public/sounds/turn-off.mp3';
13 |
14 | export const SideBar: React.FC = () => {
15 | const { route } = useRouter();
16 | const { ToggleTheme, theme } = useTheme();
17 | const [play] = useSound(theme.title === 'dark' ? turnOffSound : turnOnSound)
18 |
19 | function handleClick(){
20 | ToggleTheme()
21 | play()
22 | }
23 |
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {theme.title === 'light' ? : }
48 |
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/src/contexts/ChallengesContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, ReactNode, useContext, useEffect, useState } from 'react';
2 | import useSound from 'use-sound';
3 | import challenges from '../../challenges.json';
4 | import Cookies from 'js-cookie';
5 |
6 | import notifications from '../../public/sounds/notification.mp3';
7 | import LevelUpModal from '../components/LevelUpModal';
8 | import { useSession } from './SessionContext';
9 | interface Challenge {
10 | type: 'body' | 'eye';
11 | description: string;
12 | amount: number;
13 | }
14 |
15 | interface ProviderContextData {
16 | level: number;
17 | currentExperience: number;
18 | challengesCompleted: number;
19 | experienceToNextLevel: number;
20 | activeChallenge: Challenge;
21 | levelUp(): void;
22 | startNewChallenge(): void;
23 | resetChallenge(): void;
24 | completeChallenge(): void;
25 | closeLevelUpModal(): void;
26 | }
27 | interface ChallengesProviderProps {
28 | children: ReactNode
29 | username: string,
30 | level: number,
31 | currentExperience: number,
32 | challengesCompleted: number,
33 | name: string
34 | avatar_url: string
35 | }
36 |
37 | export const ChallengesContext = createContext({} as ProviderContextData);
38 |
39 | export const ChallengesProvider: React.FC = ({ children, ...rest }: ChallengesProviderProps) => {
40 | const { user, updateUser } = useSession();
41 | const [level, setLevel] = useState(rest.level ?? 1);
42 |
43 | const [play] = useSound(notifications)
44 | const [currentExperience, setCurrentExperience] = useState(rest.currentExperience ?? 0);
45 |
46 | const [challengesCompleted, setChallengesCompleted] = useState(rest.challengesCompleted ?? 0);
47 | const [activeChallenge, setActiveChallenge] = useState(null)
48 | const [isLevelModalOpen, setIsLevelModalOpen] = useState(false)
49 |
50 | const experienceToNextLevel = Math.pow((level + 1) * 4, 2)
51 |
52 | useEffect(() => {
53 | Notification.requestPermission();
54 | },[])
55 |
56 | useEffect(() => {
57 | const formattedUser = {
58 | level,
59 | currentExperience,
60 | challengesCompleted,
61 | username: rest?.username || '',
62 | name: rest?.name || '',
63 | avatar_url: rest?.avatar_url || '',
64 | }
65 |
66 | if(rest.username){
67 | updateUser( rest.username ,formattedUser ).then(({ value }) => {
68 | Cookies.set("user", JSON.stringify(value))
69 | })
70 | }
71 |
72 | },[level, currentExperience, challengesCompleted])
73 |
74 | function levelUp() {
75 | setLevel(level + 1);
76 | setIsLevelModalOpen(true)
77 | }
78 |
79 | function closeLevelUpModal() {
80 | setIsLevelModalOpen(false)
81 | }
82 |
83 | function startNewChallenge() {
84 | const randomChallengeIndex = Math.floor(Math.random() * challenges.length);
85 | const challenge = challenges[randomChallengeIndex]
86 |
87 | setActiveChallenge(challenge);
88 |
89 | play();
90 |
91 | if(Notification.permission === 'granted'){
92 | new Notification('Novo desafio 🎉', {
93 | body: `Valendo ${challenge.amount}xp!`
94 | })
95 | }
96 | }
97 |
98 | function resetChallenge() {
99 | setActiveChallenge(null);
100 | }
101 |
102 | function completeChallenge() {
103 | if(!activeChallenge){
104 | return;
105 | }
106 | const { amount } = activeChallenge;
107 | let finalExperience = currentExperience + amount;
108 |
109 | if(finalExperience >= experienceToNextLevel){
110 | finalExperience = finalExperience - experienceToNextLevel;
111 | levelUp();
112 | }
113 |
114 | setCurrentExperience(finalExperience);
115 | setChallengesCompleted(challengesCompleted + 1);
116 | setActiveChallenge(null);
117 | }
118 |
119 | return (
120 |
132 | {children}
133 |
134 | {isLevelModalOpen && }
135 |
136 | );
137 | };
138 |
139 | export function useProvider(): ProviderContextData {
140 | const context = useContext(ChallengesContext);
141 |
142 | return context;
143 | }
--------------------------------------------------------------------------------
/src/contexts/CountDownContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, useContext, useEffect, useState } from 'react';
2 | import { useProvider } from './ChallengesContext';
3 |
4 | interface CountDownContextData {
5 | minutes:number,
6 | seconds: number,
7 | hasFinished: boolean,
8 | isActive: boolean,
9 | startCountDown(): void,
10 | resetCountDown(): void,
11 | }
12 |
13 | export const CountDownContext = createContext({} as CountDownContextData);
14 |
15 | let countdownTimeout: NodeJS.Timeout;
16 |
17 | export const CountDownProvider: React.FC = ({ children }) => {
18 | const { startNewChallenge } = useProvider();
19 |
20 | const [ time, setTime ] = useState(.05 * 60);
21 | const [ isActive, setIsActive ] = useState(false);
22 | const [ hasFinished, setHasFinished ] = useState(false);
23 |
24 | const minutes = Math.floor(time / 60);
25 | const seconds = time % 60;
26 |
27 | useEffect(() => {
28 | if(isActive && time > 0){
29 | countdownTimeout = setTimeout(() => {
30 | setTime(time - 1)
31 | }, 1000);
32 | } else if (isActive && time === 0 ) {
33 | setHasFinished(true)
34 | setIsActive(false)
35 | startNewChallenge()
36 | }
37 | },[isActive, time])
38 |
39 | function startCountDown() {
40 | setIsActive(true);
41 | }
42 |
43 | function resetCountDown() {
44 | clearTimeout(countdownTimeout);
45 | setIsActive(false);
46 | setHasFinished(false)
47 | setTime(.05 * 60);
48 | }
49 |
50 | return (
51 |
59 | {children}
60 |
61 | );
62 | };
63 |
64 | export function useCountDown(): CountDownContextData {
65 | const context = useContext(CountDownContext);
66 |
67 | return context;
68 | }
--------------------------------------------------------------------------------
/src/contexts/SessionContext.tsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { createContext, useCallback, useContext, useState } from 'react';
3 | import axios from 'axios';
4 | import Cookies from 'js-cookie';
5 |
6 | interface IUser{
7 | // _id: string,
8 | username: string,
9 | level: number,
10 | currentExperience: number,
11 | challengesCompleted: number,
12 | name: string
13 | avatar_url: string
14 | }
15 | interface IUpdateResponse{
16 | value: IUser
17 | }
18 | interface SessionContextData {
19 | checkIfUserExists(username: string): Promise;
20 | createUser(username: string): Promise;
21 | updateUser( username: string, user: IUser): Promise;
22 | singIn(username: string): Promise,
23 | singOut(): void,
24 | user: IUser
25 | }
26 |
27 | export const SessionContext = createContext({} as SessionContextData);
28 |
29 | export const SessionProvider: React.FC = ({ children }) => {
30 | const route = useRouter();
31 | const [ user, setUser ] = useState();
32 |
33 | const checkIfUserExists = useCallback( async (username) => {
34 | try{
35 | const { data } = await axios.get(`/api/users/search?username=${username}`);
36 |
37 | if(data){
38 | return data;
39 | }else{
40 | return false;
41 | }
42 | }catch(err){
43 | console.log(err)
44 | }
45 | }, [])
46 |
47 | const createUser = useCallback( async (username) => {
48 | try{
49 | const response = await fetch(`https://api.github.com/users/${username}`);
50 | const userData = await response.json();
51 |
52 | if(userData.name){
53 | const { data } = await axios.post('/api/users/create', {
54 | data: {
55 | username,
56 | name: userData.name,
57 | avatar_url: userData.avatar_url,
58 | level: 1,
59 | currentExperience: 0,
60 | challengesCompleted: 0,
61 | active: true
62 | }
63 | })
64 |
65 | return data;
66 | }else{
67 | throw new Error("Github username não existe")
68 | }
69 | }catch(err){
70 | console.log(err)
71 | }
72 | }, [])
73 |
74 | const updateUser = useCallback( async (username, user) => {
75 | try{
76 | const { data } = await axios.put('/api/users/update', {
77 | username,
78 | data: user
79 | })
80 |
81 | return data;
82 | }catch(err){
83 | console.log(err)
84 | }
85 | }, [])
86 |
87 | const singIn = useCallback( async (username) => {
88 | const userExists = await checkIfUserExists(username)
89 |
90 | if(!userExists){
91 | const data = await createUser(username)
92 |
93 | setUser(data)
94 | Cookies.set("user", JSON.stringify(data))
95 | route.push(`/home`);
96 | }else {
97 | setUser(userExists)
98 | Cookies.set("user", JSON.stringify(userExists))
99 | route.push(`/home`);
100 | }
101 | }, [])
102 |
103 | const singOut = useCallback( async () => {
104 | setUser(null)
105 | Cookies.set("user", '')
106 | route.push(`/`);
107 | }, [])
108 |
109 | return (
110 |
118 | {children}
119 |
120 | );
121 | };
122 |
123 | export function useSession(): SessionContextData {
124 | const context = useContext(SessionContext);
125 |
126 | return context;
127 | }
--------------------------------------------------------------------------------
/src/contexts/theme.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | useContext, createContext, useState, useEffect,
3 | } from 'react';
4 | import { DefaultTheme, ThemeProvider } from 'styled-components';
5 | import { dark, light} from '../styles/theme';
6 |
7 | interface ThemeContextData {
8 | theme: DefaultTheme;
9 | ToggleTheme(): void;
10 | }
11 |
12 | const ThemeContext = createContext({} as ThemeContextData);
13 |
14 | export const ThemesProvider: React.FC = ({ children }) => {
15 | const [theme, setTheme] = useState(light);
16 |
17 | useEffect(() => {
18 | const themeLocal = localStorage.getItem('@MoveYourself:theme');
19 |
20 | setTheme(themeLocal === 'light' ? light : dark);
21 | }, []);
22 |
23 | const ToggleTheme = () => {
24 | if (theme.title === 'light') {
25 | localStorage.setItem('@MoveYourself:theme', dark.title);
26 | setTheme(dark);
27 | } else {
28 | localStorage.setItem('@MoveYourself:theme', light.title);
29 | setTheme(light);
30 | }
31 | };
32 |
33 | return (
34 |
35 |
36 | {children}
37 |
38 |
39 | );
40 | };
41 |
42 | export function useTheme(): ThemeContextData {
43 | const context = useContext(ThemeContext);
44 |
45 | return context;
46 | }
--------------------------------------------------------------------------------
/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import type { AppProps } from 'next/app'
2 | import { GlobalStyle } from '../styles/global';
3 | import { ThemesProvider } from '../contexts/theme';
4 | import { SessionProvider } from '../contexts/SessionContext';
5 |
6 | export default function App({ Component, pageProps }: AppProps) {
7 | return (
8 | <>
9 |
10 |
11 |
12 |
13 |
14 |
15 | >
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/src/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import Document, {
2 | DocumentInitialProps,
3 | DocumentContext,
4 | Html,
5 | Head,
6 | Main,
7 | NextScript } from 'next/document'
8 | import { ServerStyleSheet } from 'styled-components'
9 |
10 | export default class MyDocument extends Document {
11 | static async getInitialProps(ctx: DocumentContext): Promise {
12 | const sheet = new ServerStyleSheet()
13 | const originalRenderPage = ctx.renderPage
14 |
15 | try {
16 | ctx.renderPage = () =>
17 | originalRenderPage({
18 | enhanceApp: (App) => (props) =>
19 | sheet.collectStyles( ),
20 | })
21 |
22 | const initialProps = await Document.getInitialProps(ctx)
23 | return {
24 | ...initialProps,
25 | styles: (
26 | <>
27 | {initialProps.styles}
28 | {sheet.getStyleElement()}
29 | >
30 | ),
31 | }
32 | } finally {
33 | sheet.seal()
34 | }
35 | }
36 |
37 | render(): JSX.Element {
38 | return (
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | )
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/pages/api/users/all.ts:
--------------------------------------------------------------------------------
1 | import { NowRequest, NowResponse } from '@vercel/node';
2 | import { MongoClient } from 'mongodb';
3 | import url from 'url';
4 |
5 | let cachedDb = null;
6 |
7 | async function connectToDatabase(uri: string){
8 | if(cachedDb){
9 | return cachedDb;
10 | }
11 |
12 | const client = await MongoClient.connect(uri, {
13 | useNewUrlParser: true,
14 | useUnifiedTopology: true,
15 | })
16 |
17 | const dbName = url.parse(uri).pathname.substring(1);
18 |
19 | const db = client.db(dbName);
20 |
21 | cachedDb = db;
22 |
23 | return db;
24 | }
25 |
26 | export default async (req: NowRequest, res: NowResponse) => {
27 | try{
28 | const db = await connectToDatabase(process.env.MONOGODB_URL);
29 |
30 | const collection = db.collection('data')
31 |
32 | const myCursor = collection.find().sort( { level: -1 } );
33 |
34 | let users = []
35 | await myCursor.forEach((item) => {
36 | users = [...users, item]
37 | });
38 |
39 | if(users){
40 | return res.status(200).json(users);
41 | }
42 | return res.status(204).json({ message : "user does not exist"});
43 |
44 | }catch(err){
45 | return res.status(400).json({
46 | message: err.message || "Unexpected error."
47 | })
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/pages/api/users/create.ts:
--------------------------------------------------------------------------------
1 | import { NowRequest, NowResponse } from '@vercel/node';
2 | import { MongoClient } from 'mongodb';
3 | import url from 'url';
4 |
5 | let cachedDb = null;
6 |
7 | async function connectToDatabase(uri: string){
8 | if(cachedDb){
9 | return cachedDb;
10 | }
11 |
12 | const client = await MongoClient.connect(uri, {
13 | useNewUrlParser: true,
14 | useUnifiedTopology: true,
15 | })
16 |
17 | const dbName = url.parse(uri).pathname.substring(1);
18 |
19 | const db = client.db(dbName);
20 |
21 | cachedDb = db;
22 |
23 | return db;
24 | }
25 |
26 | export default async function CreateUser(req: NowRequest, res: NowResponse){
27 | try{
28 | const { data } = req.body;
29 |
30 | const db = await connectToDatabase(process.env.MONOGODB_URL);
31 |
32 | const collection = db.collection('data')
33 |
34 | const { ops } = await collection.insertOne( data )
35 |
36 | return res.status(201).json(ops[0]);
37 | }catch(err){
38 | return res.status(400).json({
39 | message: err.message || "Unexpected error."
40 | })
41 | }
42 | }
--------------------------------------------------------------------------------
/src/pages/api/users/search.ts:
--------------------------------------------------------------------------------
1 | import { NowRequest, NowResponse } from '@vercel/node';
2 | import { MongoClient } from 'mongodb';
3 | import url from 'url';
4 |
5 | let cachedDb = null;
6 |
7 | async function connectToDatabase(uri: string){
8 | if(cachedDb){
9 | return cachedDb;
10 | }
11 |
12 | const client = await MongoClient.connect(uri, {
13 | useNewUrlParser: true,
14 | useUnifiedTopology: true,
15 | })
16 |
17 | const dbName = url.parse(uri).pathname.substring(1);
18 |
19 | const db = client.db(dbName);
20 |
21 | cachedDb = db;
22 |
23 | return db;
24 | }
25 |
26 | export default async function SearchUser(req: NowRequest, res: NowResponse){
27 | const { username } = req.query;
28 |
29 | try{
30 | const db = await connectToDatabase(process.env.MONOGODB_URL);
31 |
32 | const collection = db.collection('data')
33 |
34 | const userExists = await collection.findOne({ username })
35 |
36 | if(userExists){
37 | return res.status(200).json(userExists);
38 | }
39 |
40 | return res.status(204).json({ message : "user does not exist"});
41 | }catch(err){
42 | return res.status(400).json({
43 | message: err.message || "Unexpected error."
44 | })
45 | }
46 | }
--------------------------------------------------------------------------------
/src/pages/api/users/update.ts:
--------------------------------------------------------------------------------
1 | import { NowRequest, NowResponse } from '@vercel/node';
2 | import { MongoClient } from 'mongodb';
3 | import url from 'url';
4 |
5 | let cachedDb = null;
6 |
7 | async function connectToDatabase(uri: string){
8 | if(cachedDb){
9 | return cachedDb;
10 | }
11 |
12 | const client = await MongoClient.connect(uri, {
13 | useNewUrlParser: true,
14 | useUnifiedTopology: true,
15 | })
16 |
17 | const dbName = url.parse(uri).pathname.substring(1);
18 |
19 | const db = client.db(dbName);
20 |
21 | cachedDb = db;
22 |
23 | return db;
24 | }
25 |
26 | export default async function UpdateUser(req: NowRequest, res: NowResponse){
27 | try{
28 | const { username, data } = req.body;
29 |
30 | const db = await connectToDatabase(process.env.MONOGODB_URL);
31 |
32 | const collection = db.collection('data')
33 |
34 | const user = await collection.findOneAndUpdate(
35 | { username },
36 | { $set: data },
37 | { returnOriginal: false },
38 | );
39 |
40 | return res.status(200).json(user);
41 | }catch(err){
42 | return res.status(400).json({
43 | message: err.message || "Unexpected error."
44 | })
45 | }
46 | }
--------------------------------------------------------------------------------
/src/pages/home.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Router from 'next/router';
3 | import Head from 'next/head'
4 | import { GetServerSideProps } from 'next';
5 |
6 | import { ExperienceBar } from '../components/ExperienceBar'
7 | import { SideBar } from '../components/SideBar'
8 | import Profile from '../components/Profile'
9 | import { CompletedChallenges } from '../components/CompletedChallenges'
10 | import { CountDown } from '../components/CountDown'
11 |
12 | import { ChallengeBox } from '../components/ChallengeBox'
13 | import { CountDownProvider } from '../contexts/CountDownContext';
14 | import { ChallengesProvider } from '../contexts/ChallengesContext';
15 |
16 | import { Container, LeftSide, RightSide } from '../styles/pages/home'
17 | import { useSession } from '../contexts/SessionContext';
18 | interface IProps {
19 | username: string
20 | name: string
21 | avatar_url: string
22 | level: number
23 | currentExperience: number
24 | challengesCompleted: number
25 | }
26 |
27 | const Home: React.FC = (props) => {
28 | return (
29 |
30 |
31 |
32 | Home | Move YourSelf
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
55 |
56 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
57 | const { user } = await ctx.req.cookies;
58 |
59 | if (!user) {
60 | if(typeof window === 'undefined'){
61 | ctx.res.writeHead(302, { Location: '/' })
62 | ctx.res.end()
63 | }else{
64 | Router.push('/')
65 | }
66 | return {
67 | props: {}
68 | }
69 | }else{
70 | const userFormatted = JSON.parse(user)
71 |
72 | return {
73 | props: {
74 | ...userFormatted
75 | }
76 | }
77 | };
78 | }
79 |
80 | export default Home;
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { GetServerSideProps } from 'next';
2 | import Router from 'next/router';
3 | import Head from 'next/head'
4 | import React, { useState } from 'react'
5 | import useSound from 'use-sound'
6 | import { FiGithub, FiMoon, FiSun } from "react-icons/fi";
7 |
8 | import Input from '../components/Input';
9 |
10 | import { useTheme } from '../contexts/theme';
11 | import { useSession } from '../contexts/SessionContext';
12 |
13 | import turnOffSound from '../../public/sounds/turn-off.mp3';
14 | import turnOnSound from '../../public/sounds/turn-on.mp3';
15 |
16 | import { Container, LeftSide, RightSide, TitleContainer } from '../styles/pages'
17 |
18 | const Index: React.FC = () => {
19 | const { singIn } = useSession();
20 | const {theme, ToggleTheme} = useTheme();
21 |
22 | const [username, setUsername]= useState('');
23 |
24 | const [play] = useSound(theme.title === 'dark' ? turnOffSound : turnOnSound)
25 |
26 | function handleClick(){
27 | ToggleTheme()
28 | play()
29 | }
30 |
31 | async function handleUsername(e){
32 | e.preventDefault();
33 | if(username){
34 | await singIn(username)
35 | }
36 | }
37 |
38 | return (
39 |
40 |
41 | Início | Move YourSelf
42 |
43 |
44 |
45 |
46 | {theme.title === 'light' ? : }
47 |
48 |
49 |
50 |
51 |
52 |
53 | Bem-vindo
54 |
55 |
56 |
57 | Faça login com seu Github para começar
58 |
59 |
60 |
67 |
68 |
69 |
70 |
71 | )
72 | }
73 |
74 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
75 | const {user} = await ctx.req.cookies;
76 |
77 | if (user) {
78 | if(typeof window === 'undefined'){
79 | ctx.res.writeHead(302, { Location: '/home' })
80 | ctx.res.end()
81 | }else{
82 | Router.push('/home')
83 | }
84 | return { props: {} }
85 | }else{
86 | return { props: {} }
87 | };
88 | }
89 |
90 | export default Index;
--------------------------------------------------------------------------------
/src/pages/leaderboard.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import axios from 'axios';
3 | import React, { useEffect, useState } from 'react'
4 |
5 | import { SideBar } from '../components/SideBar'
6 | import { Loading } from '../components/Loading'
7 | import { Container } from '../styles/pages/leaderboard'
8 |
9 | const Leaderboard: React.FC = () => {
10 |
11 | const [ loading , setLoading] = useState(false);
12 | const [ users , setUsers] = useState([]);
13 |
14 | useEffect(() => {
15 | setLoading(true)
16 | axios.get('/api/users/all').then(({ data }) => {
17 | setUsers( data )
18 | setLoading(false)
19 | })
20 | },[])
21 |
22 | return (
23 |
24 |
25 | Leaderboard | Move YourSelf
26 |
27 |
28 |
29 |
30 | Leaderboard
31 |
32 |
33 | {loading ? : (
34 |
35 |
36 |
37 | POSIÇÃO
38 | USUÁRIO
39 | DESAFIOS
40 | EXPERIÊNCIA ATUAl
41 |
42 |
43 |
44 |
45 | {users.map((item, index) => (
46 |
47 | {index + 1}
48 |
49 |
50 |
51 |
{item.name}
52 |
53 |
54 |
Level {item.level}
55 |
56 |
57 |
58 |
59 | {item.challengesCompleted}
60 | {' Completados'}
61 |
62 |
63 | {item.currentExperience}
64 | {' xp'}
65 |
66 |
67 | ))}
68 |
69 |
70 | )}
71 |
72 |
73 | )
74 | }
75 |
76 | export default Leaderboard;
--------------------------------------------------------------------------------
/src/styles/components/ChallengeBox/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | height: 100%;
5 |
6 | background: ${({ theme }) => theme.colors.backgroundLight};
7 | border-radius: 5px;
8 | box-shadow: 0 0 60px rgba(0, 0, 0, 0.05);
9 | padding: 1.5rem 2rem;
10 |
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | justify-content: center;
15 |
16 | text-align: center;
17 |
18 | `;
19 |
20 | export const ChallengeNotActive = styled.div`
21 |
22 | display: flex;
23 | flex-direction: column;
24 | align-items: center;
25 |
26 | strong {
27 | font-size: 1.5rem;
28 | font-weight: 500;
29 | line-height: 1.4;
30 | }
31 |
32 | p{
33 | display: flex;
34 | flex-direction: column;
35 | align-items: center;
36 | line-height: 1.4;
37 | max-width: 70%;
38 | margin-top: 3rem;
39 |
40 | img {
41 | margin-bottom: 1rem;
42 | }
43 | }
44 | `;
45 |
46 | export const ChallengeActive = styled.div`
47 | height: 100%;
48 |
49 | display: flex;
50 | flex-direction: column;
51 |
52 | header {
53 | color: ${({ theme }) => theme.colors.blue};
54 |
55 | font-weight: 600;
56 | font-size: 1.25rem;
57 | padding: 0 2rem 1.5rem;
58 | border-bottom: 1px solid ${({ theme }) => theme.colors.grayLine}
59 | }
60 |
61 | main {
62 | flex: 1;
63 | display: flex;
64 | flex-direction: column;
65 | align-items: center;
66 | justify-content: center;
67 |
68 | strong {
69 | font-weight: 600;
70 | font-size: 2rem;
71 | color: ${({ theme }) => theme.colors.title};
72 |
73 | margin: 1.5rem 0 1rem;
74 | }
75 |
76 | p {
77 | line-height: 1.5;
78 | }
79 | }
80 |
81 | footer{
82 | display: grid;
83 | grid-template-columns: 1fr 1fr;
84 | gap: 1rem;
85 |
86 | button {
87 | height: 3rem;
88 | display: flex;
89 | align-items: center;
90 | justify-content: center;
91 |
92 | border: 0;
93 | border-radius: 5px;
94 |
95 | color: ${({ theme }) => theme.colors.white};
96 |
97 | font-size: 1rem;
98 | font-weight: 600;
99 |
100 | transition: filter 0.3s ease;
101 |
102 | &:hover{
103 | filter: brightness(0.9)
104 | }
105 | }
106 | }
107 | `;
108 |
109 | export const FailedButton = styled.button`
110 | background: ${({ theme }) => theme.colors.red};
111 | `;
112 |
113 | export const SucceededButton = styled.button`
114 | background: ${({ theme }) => theme.colors.green};
115 | `;
--------------------------------------------------------------------------------
/src/styles/components/CompletedChallenges/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | display: flex;
5 | align-items: center;
6 | justify-content: space-between;
7 |
8 | margin: 3.5rem 0;
9 | padding-bottom: 1rem;
10 |
11 | border-bottom: 1px solid #d7e8ea;
12 |
13 | font-weight: 500;
14 |
15 | span:first-child{
16 | font-size: 1.25rem;
17 | }
18 |
19 | span:last-child{
20 | font-size: 1.5rem;
21 | }
22 | `;
--------------------------------------------------------------------------------
/src/styles/components/CountDown/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const CountDownContainer = styled.div`
4 | display: flex;
5 | align-items: center;
6 |
7 | font-family: 'Rajdhani';
8 | font-weight: 600;
9 | color: ${({ theme }) => theme.colors.titleTimer};
10 |
11 | > div {
12 | flex: 1;
13 |
14 | display: flex;
15 | align-items: center;
16 | justify-content: space-evenly;
17 |
18 | background: ${({ theme }) => theme.colors.backgroundLight};
19 | box-shadow: 0 0 60px rgba(0, 0, 0, 0.05);
20 | border-radius: 5px;
21 | font-size: 8.5rem;
22 | text-align: center;
23 |
24 | span:first-child {
25 | border-right: 1px solid #f0f2f3;
26 | }
27 | span:last-child {
28 | border-left: 1px solid #f0f2f3;
29 | }
30 |
31 | span {
32 | flex: 1;
33 | }
34 | }
35 |
36 | > span {
37 | font-size: 6.25rem;
38 | margin: 0 0.5rem;
39 | }
40 | `;
41 |
42 | export const CountDownButton = styled.button`
43 | width: 100%;
44 | height: 5rem;
45 | margin-top: 2rem;
46 | display: flex;
47 | align-items: center;
48 | justify-content: center;
49 |
50 | border: 0;
51 | border-radius: 5px;
52 | background: ${({ theme }) => theme.colors.blue};
53 | color: ${({ theme }) => theme.colors.white};
54 |
55 | font-size: 1.5rem;
56 | font-weight: 600;
57 |
58 | transition: background-color 0.2s ease;
59 |
60 | &:hover{
61 | background: ${({ theme }) => theme.colors.blueDark}
62 | }
63 | `;
64 |
65 |
66 | export const StopCountDownButton = styled.button`
67 | width: 100%;
68 | height: 5rem;
69 | margin-top: 2rem;
70 | display: flex;
71 | align-items: center;
72 | justify-content: center;
73 |
74 | border: 0;
75 | border-radius: 5px;
76 | background: ${({ theme }) => theme.colors.backgroundLight};
77 | color: ${({ theme }) => theme.colors.titleButton};
78 |
79 | font-size: 1.5rem;
80 | font-weight: 600;
81 |
82 | transition: background-color 0.2s ease;
83 |
84 | &:hover{
85 | background: ${({ theme }) => theme.colors.red};
86 | color: ${({ theme }) => theme.colors.white};
87 | }
88 | `;
89 |
90 | export const FinishedCountDownButton = styled.button`
91 | width: 100%;
92 | height: 5rem;
93 | margin-top: 2rem;
94 | display: flex;
95 | align-items: center;
96 | justify-content: center;
97 |
98 | border: 0;
99 | border-radius: 5px;
100 | background: ${({ theme }) => theme.colors.backgroundLight};
101 | border-bottom: 5px solid ${({ theme }) => theme.colors.green};
102 | color: ${({ theme }) => theme.colors.titleButton};
103 |
104 | font-size: 1.5rem;
105 | font-weight: 600;
106 |
107 | cursor: default;
108 | `;
--------------------------------------------------------------------------------
/src/styles/components/ExperienceBar/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.header`
4 | display: flex;
5 | align-items: center;
6 |
7 | span {
8 | font-size: 1rem;
9 | }
10 |
11 | > div{
12 | flex: 1;
13 | height: 4px;
14 | border-radius: 4px;
15 | background: ${({ theme }) => theme.colors.grayLine};
16 | margin: 0 1.5rem;
17 | position: relative;
18 |
19 | div {
20 | height: 4px;
21 | width:0;
22 | border-radius: 4px;
23 | background: ${({ theme }) => theme.colors.green};
24 | }
25 |
26 | span {
27 | position: absolute;
28 | width: max-content;
29 | top: 12px;
30 | transform: translateX( -50% );
31 | }
32 | }
33 |
34 | @media (max-width: 800px){
35 | margin-top: 4rem;
36 | }
37 | `;
--------------------------------------------------------------------------------
/src/styles/components/Input/index.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | interface ContainerProps {
4 | isFocused: boolean;
5 | isFilled: boolean;
6 | isLoading: boolean;
7 | }
8 |
9 | export const Container = styled.div`
10 | display: flex;
11 | align-items: center;
12 |
13 | input {
14 | border: 0;
15 | background: linear-gradient( 90deg, ${({theme}) => theme.colors.inputBackground} 0%, ${({theme}) => theme.colors.inputBackgroundFinish} 100%);
16 | font-size: 1.3rem;
17 | padding: 1.65rem;
18 |
19 | border-radius: 5px 0px 0px 5px;
20 |
21 | color: ${({theme}) => theme.colors.white};
22 | border: 1.5px solid transparent;
23 |
24 | /* outline-width: 0; */
25 | outline-color: ${({ theme }) => theme.colors.inputBackground};
26 |
27 | &:hover{
28 | border: 1.5px solid ${({ theme }) => theme.colors.inputBackground};
29 | }
30 |
31 | &::placeholder{
32 | color: ${({theme}) => theme.colors.white};
33 | opacity: 0.5;
34 | }
35 | }
36 |
37 | button {
38 | display: flex;
39 | align-items:center;
40 | justify-content: center;
41 | border: 1.5px solid ${({ theme }) => theme.colors.inputBackground};
42 | outline-color: ${({ theme }) => theme.colors.inputBackground};
43 |
44 | padding: 1.66rem;
45 | font-size: 2rem;
46 | background: ${({ theme }) => theme.colors.inputBackground};
47 | color: ${({theme}) => theme.colors.white };
48 | border-radius: 0px 5px 5px 0px;
49 |
50 | svg {
51 | width: 1.5rem;
52 | height: 1.5rem;
53 | ${(props) =>
54 | props.isLoading &&
55 |
56 | css`
57 | @keyframes Rote {
58 | 0% {
59 | transform: rotate(0deg);
60 | }
61 | 100% {
62 | transform: rotate(360deg);
63 | }
64 | }
65 | animation: Rote 1s infinite;
66 | `}
67 | }
68 |
69 | ${(props) =>
70 | props.isFilled &&
71 | css`
72 | border: 1.5px solid ${({ theme }) => theme.colors.green};
73 | background: ${({ theme }) => theme.colors.green};
74 | `}
75 | }
76 |
77 | @media (max-width: 520px){
78 | input {
79 | width: 78%;
80 | }
81 | }
82 | `;
--------------------------------------------------------------------------------
/src/styles/components/LevelUpModal/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Overlay = styled.header`
4 | background: ${({ theme }) => theme.colors.backgroundModal};
5 | /* background: rgba(242, 243, 245, 0.8); */
6 | position: fixed;
7 | top: 0;
8 | bottom: 0;
9 | right: 0;
10 | left: 0;
11 |
12 | display: flex;
13 | justify-content: center;
14 | align-items: center;
15 | `;
16 |
17 | export const Container = styled.header`
18 | background: ${({ theme }) => theme.colors.backgroundModalContent};
19 | width: 100%;
20 | max-width:400px;
21 | padding: 2rem 3rem;
22 | border-radius: 5px;
23 | box-shadow: 0 0 60px rgba(0, 0, 0, 0.05);
24 | text-align: center;
25 | position: relative;
26 |
27 | header {
28 | font-size: 8.75rem;
29 | font-weight: 600;
30 |
31 | color: ${({ theme }) => theme.colors.blue};
32 | background: url('/icons/levelup.svg') no-repeat center;
33 | background-size: contain;
34 |
35 | text-shadow: 0px 10px 16px rgba(89, 101, 224, 0.3);
36 | }
37 |
38 | strong{
39 | font-size: 2.25rem;
40 | color: ${({ theme }) => theme.colors.title};
41 | }
42 |
43 | p {
44 | font-size: 1.25rem;
45 | color: ${({ theme }) => theme.colors.text};
46 | margin-top: 0.25rem;
47 | }
48 |
49 | button {
50 | position: absolute;
51 | right: 0.5rem;
52 | top: 0.5rem;
53 | background: transparent;
54 | border:0;
55 | font-size: 0;
56 | }
57 | `;
--------------------------------------------------------------------------------
/src/styles/components/Loading/index.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | @keyframes teste{
5 | 0% {
6 | background-position: 0% 0%;
7 | }
8 | 100% {
9 | background-position: 195px 0%;
10 | }
11 | };
12 |
13 | /* section >div{
14 | display: flex;
15 | } */
16 |
17 | table{
18 | border-spacing: 0 8px;
19 | border-radius: 5px;
20 | min-width: 650px;
21 | width: 100%;
22 |
23 | div, span, img, strong{
24 | background: linear-gradient(
25 | -90deg,
26 | ${({theme}) => theme.colors.background },
27 | ${({theme}) => theme.colors.backgroundLight },
28 | ${({theme}) => theme.colors.background }
29 | );
30 | animation: teste 1.2s ease-in-out infinite;
31 | }
32 |
33 | .separator{
34 | height: 24px;
35 | }
36 |
37 | thead{
38 | font-style: normal;
39 | font-weight: bold;
40 | font-size: 14px;
41 | line-height: 17px;
42 | text-transform: uppercase;
43 | color: ${({theme}) => theme.colors.text };
44 | opacity: 0.5;
45 | }
46 |
47 | tbody{
48 | margin-top: 24px;
49 | background: ${({theme}) => theme.colors.backgroundLight };
50 | border-radius: 5px;
51 |
52 | tr{
53 | border-radius: 5px;
54 | }
55 |
56 | th {
57 | padding: 16px 24px;
58 |
59 | &:first-child{
60 | padding: 10px;
61 | position: relative;
62 | &::before{
63 | top: 40%;
64 | left: 20%;
65 | margin: auto;
66 | animation: teste 1.2s ease-in-out infinite;
67 | content:"";
68 | height: 20px;
69 | width: 50px;
70 | display: flex;
71 | position: absolute;
72 | background: linear-gradient(
73 | -90deg,
74 | ${({theme}) => theme.colors.background },
75 | ${({theme}) => theme.colors.backgroundLight },
76 | ${({theme}) => theme.colors.background }
77 | );
78 | }
79 | }
80 |
81 | > span{
82 | position:relative;
83 | &::before{
84 | animation: teste 1.2s ease-in-out infinite;
85 | content:"";
86 | height: 20px;
87 | top: 50%;
88 | left: 50%;
89 | width: 100px;
90 | display: flex;
91 | position: absolute;
92 | background: linear-gradient(
93 | -90deg,
94 | ${({theme}) => theme.colors.background },
95 | ${({theme}) => theme.colors.backgroundLight },
96 | ${({theme}) => theme.colors.background }
97 | );
98 | }
99 | }
100 |
101 | &:first-child {
102 | border-radius: 5px 0px 0px 5px;
103 | border-right: 2px solid ${({theme}) => theme.colors.background };
104 | }
105 |
106 | &:last-child {
107 | border-radius: 0px 5px 5px 0px;
108 | }
109 |
110 | &:nth-child(2) {
111 | display: flex;
112 | align-items: center;
113 | justify-content:center;
114 |
115 | > div:first-child {
116 | width: 64px;
117 | height: 64px;
118 | border-radius: 50%;
119 | margin-right: 16px;
120 | }
121 |
122 | > div{
123 | display: flex;
124 | flex-direction: column;
125 | margin-right: auto;
126 |
127 | strong {
128 | position: relative;
129 | &::before{
130 | animation: teste 1.2s ease-in-out infinite;
131 | content:"";
132 | height: 20px;
133 | width: 200px;
134 | display: flex;
135 | position: absolute;
136 | background: linear-gradient(
137 | -90deg,
138 | ${({theme}) => theme.colors.background },
139 | ${({theme}) => theme.colors.backgroundLight },
140 | ${({theme}) => theme.colors.background }
141 | );
142 | }
143 | }
144 | }
145 | }
146 | }
147 | }
148 | }
149 |
150 | @media (max-width: 800px){
151 |
152 | }
153 | `;
--------------------------------------------------------------------------------
/src/styles/components/Profile/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | display: flex;
5 | align-items: center;
6 |
7 | > img {
8 | width: 5.5rem;
9 | height: 5.5rem;
10 | border-radius: 50%;
11 | }
12 |
13 | div{
14 | margin-left: 1.5rem;
15 |
16 | strong {
17 | font-size: 1.5rem;
18 | font-weight: 600;
19 | color: ${({theme}) => theme.colors.title }
20 | }
21 |
22 | p {
23 | font-size: 1rem;
24 | margin-top: 0.5rem;
25 |
26 | > img {
27 | margin-right: 0.5rem;
28 | }
29 | }
30 | }
31 |
32 | button {
33 | background: transparent;
34 | border: 0;
35 | margin-left: auto;
36 | cursor: pointer;
37 | color: ${({ theme }) => theme.colors.titleTimer};
38 |
39 | &:hover{
40 | color: ${({ theme }) => theme.colors.red};
41 | }
42 | }
43 |
44 | @media (max-width: 800px){
45 | button {
46 | width: 2.2rem;
47 | }
48 | }
49 | `;
--------------------------------------------------------------------------------
/src/styles/components/SideBar/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.nav`
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: space-between;
7 | align-items: center;
8 |
9 | position: absolute;
10 | left: 0;
11 | top: 0;
12 |
13 | height: 100vh;
14 | width: 3rem;
15 | padding: 2.3rem 2.2rem;
16 |
17 | background: ${({ theme }) => theme.colors.backgroundLight};
18 |
19 | > svg {
20 | path{
21 | fill: ${({ theme }) => theme.colors.logoColor};
22 | }
23 | }
24 |
25 | > nav a{
26 | display: flex;
27 | cursor: pointer;
28 | position: relative;
29 |
30 | &:not(:first-child){
31 | margin-top: 16px;
32 | }
33 |
34 | &.active{
35 | color: ${({ theme }) => theme.colors.logoColor};
36 |
37 | &::before{
38 | content: "";
39 | background: ${({ theme }) => theme.colors.logoColor};
40 | border-radius: 0px 5px 5px 0px;
41 | position: absolute;
42 | left: -1.24rem;
43 | width: 4px;
44 | height: 100%;
45 |
46 | @media (max-width: 1366px){
47 | left: -1rem;
48 | }
49 | }
50 | }
51 | }
52 |
53 | button{
54 | border: 0;
55 | background: transparent;
56 | outline-color: ${({ theme }) => theme.colors.backgroundLight};
57 |
58 | svg {
59 | color: ${({ theme }) => theme.colors.text};
60 | }
61 | }
62 |
63 | @media (max-width: 800px){
64 | flex-direction: row;
65 |
66 | padding: 2rem;
67 | left: unset;
68 | right: 0;
69 | top: 0;
70 |
71 | width: 100%;
72 | height: 3rem;
73 |
74 | svg {
75 | width: 2.2rem;
76 | }
77 |
78 | > div {
79 | display: flex;
80 | align-items:center;
81 |
82 | a:last-child{
83 | margin-left: 16px;
84 | margin-top: 0;
85 | }
86 | }
87 | }
88 |
89 | @media (max-width: 800px){
90 | >nav{
91 | display: flex;
92 | align-items:center;
93 |
94 | a {
95 | margin-left: 16px;
96 |
97 | &.active::before{
98 | content: "";
99 | top: -26px;
100 | left: 13px;
101 | transform: rotate(90deg);
102 | }
103 |
104 | &:not(:first-child){
105 | margin-top: 0px;
106 | }
107 | }
108 | }
109 | }
110 | `;
--------------------------------------------------------------------------------
/src/styles/global.ts:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components'
2 |
3 | export const GlobalStyle = createGlobalStyle`
4 | * {
5 | margin: 0;
6 | padding: 0;
7 | box-sizing: border-box;
8 | }
9 |
10 | @media(max-width: 1366px){
11 | html{
12 | font-size: 83.75%
13 | }
14 | }
15 |
16 | @media(max-width: 720px){
17 | html{
18 | font-size: 87.5%
19 | }
20 | }
21 |
22 | body {
23 | background: ${({ theme }) => theme.colors.background};
24 | color: ${({ theme }) => theme.colors.text};
25 | font-family: 'Inter', sans-serif;
26 | }
27 |
28 | body, input, textarea, button{
29 | font: 400 1rem "Inter", sans-serif;
30 | }
31 |
32 | button{
33 | cursor: pointer;
34 | }
35 |
36 | a {
37 | color: inherit;
38 | text-decoration: none;
39 | }
40 | `
--------------------------------------------------------------------------------
/src/styles/pages/home.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 |
4 | export const Container = styled.div`
5 | height: 100vh;
6 | max-width: 992px;
7 | margin: 0 auto;
8 | padding: 2.5rem 2rem 2.5rem 6.5rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 |
13 | section {
14 | flex: 1;
15 | display: grid;
16 | grid-template-columns: 1fr 1fr;
17 | gap: 6.25rem;
18 | align-content: center;
19 | }
20 |
21 | @media (max-width: 992px){
22 | section {
23 | gap: 1.25rem;
24 | }
25 | }
26 |
27 | @media (max-width: 800px){
28 | padding: 1rem;
29 | section {
30 | margin-top: 3.5rem;
31 | grid-template-columns: auto;
32 | }
33 | }
34 |
35 | @media (max-width: 542px){
36 | section {
37 | grid-template-columns: auto;
38 | }
39 | }
40 | `
41 |
42 | export const LeftSide = styled.div`
43 |
44 | `;
45 |
46 | export const RightSide = styled.div`
47 | @media (max-width: 800px){
48 | margin-bottom: 1rem;
49 | }
50 | `;
51 |
--------------------------------------------------------------------------------
/src/styles/pages/index.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 |
4 | export const Container = styled.div`
5 | height: 100vh;
6 | width: 100vw;
7 | /* margin: 0 auto; */
8 | padding: 2.5rem 2rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 |
13 | background: url('/background-logo.png') no-repeat;
14 | background-size: contain;
15 | background-position: center left;
16 | background-color: ${({theme}) => theme.colors.backgroundIndex };
17 |
18 | section {
19 | max-width: 992px;
20 | margin: 0 auto;
21 | flex: 1;
22 | display: grid;
23 | grid-template-columns: 1fr 1fr;
24 | gap: 6.25rem;
25 | align-content: center;
26 | position: relative;
27 |
28 |
29 |
30 | @media (max-width: 520px){
31 |
32 | grid-template-columns: 1fr;
33 | gap: 0;
34 | }
35 | }
36 | `
37 |
38 | export const LeftSide = styled.div`
39 | /* position: relative; */
40 |
41 | button{
42 | position: absolute;
43 | right: 0;
44 | top: 0;
45 | border: 0;
46 | background: transparent;
47 | color: ${({ theme }) => theme.colors.white};
48 |
49 | }
50 | `;
51 |
52 | export const RightSide = styled.div`
53 | display :flex;
54 | justify-content: space-between;
55 | flex-direction: column;
56 |
57 | >div{
58 | display: flex;
59 | flex-direction: column;
60 |
61 | strong {
62 | margin-top: 8rem;
63 | font-size: 3rem;
64 | font-weight: 500;
65 | color: ${({theme}) => theme.colors.white };
66 | }
67 | }
68 |
69 | @media (max-width: 520px){
70 | width: 85vw;
71 | >div{
72 | strong {
73 | font-size: 2rem;
74 | }
75 | }
76 | }
77 | `;
78 |
79 | export const TitleContainer = styled.div`
80 | display: flex;
81 |
82 | margin: 3rem 0;
83 |
84 | svg{
85 | color: ${({theme}) => theme.colors.white };
86 | }
87 |
88 | span {
89 | max-width: 300px;
90 | margin-left: 1rem;
91 | font-size: 1.4rem;
92 | font-weight: 500;
93 | color: ${({theme}) => theme.colors.white };
94 | }
95 |
96 | @media (max-width: 520px){
97 | span {
98 | font-size: 1.3rem;
99 | }
100 | }
101 | `;
--------------------------------------------------------------------------------
/src/styles/pages/leaderboard.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | height: 100vh;
5 | /* width: 100vw; */
6 | /* margin: 0 auto; */
7 | overflow-x: hidden;
8 | padding: 2.5rem 2rem;
9 |
10 | display: flex;
11 | flex-direction: column;
12 |
13 | background-color: ${({theme}) => theme.colors.background };
14 |
15 | section {
16 | margin: 0 25px;
17 | flex: 1;
18 | display: flex;
19 | align-content: center;
20 | justify-content:center;
21 | flex-direction: column;
22 | padding-left: 40px;
23 |
24 | h2 {
25 | font-style: normal;
26 | font-weight: 600;
27 | font-size: 36px;
28 | line-height: 46px;
29 | color: ${({theme}) => theme.colors.titleButton };
30 | margin-bottom: 40px;
31 | }
32 |
33 | table{
34 | border-spacing: 0 8px;
35 | border-radius: 5px;
36 | min-width: 650px;
37 |
38 | .separator{
39 | height: 24px;
40 | }
41 |
42 | thead{
43 | font-style: normal;
44 | font-weight: bold;
45 | font-size: 14px;
46 | line-height: 17px;
47 | text-transform: uppercase;
48 | color: ${({theme}) => theme.colors.text };
49 | opacity: 0.5;
50 | }
51 |
52 | tbody{
53 | margin-top: 24px;
54 | background: ${({theme}) => theme.colors.backgroundLight };
55 | border-radius: 5px;
56 |
57 | tr{
58 | border-radius: 5px;
59 |
60 | th:first-child{
61 | font-weight: 500;
62 | font-size: 18px;
63 | }
64 | }
65 |
66 | th {
67 | padding: 16px;
68 |
69 | >span{
70 | font-style: normal;
71 | font-weight: 500;
72 | font-size: 16px;
73 | line-height: 19px;
74 | color: ${({theme}) => theme.colors.hightLightTable };
75 | }
76 |
77 | &:first-child {
78 | border-radius: 5px 0px 0px 5px;
79 | border-right: 2px solid ${({theme}) => theme.colors.background };
80 | }
81 |
82 | &:last-child {
83 | border-radius: 0px 5px 5px 0px;
84 | }
85 |
86 | &:nth-child(2) {
87 | display: flex;
88 | align-items: center;
89 | justify-content:center;
90 |
91 | > img {
92 | width: 64px;
93 | height: 64px;
94 | border-radius: 50%;
95 | margin-right: 16px;
96 | }
97 |
98 | > div{
99 | display: flex;
100 | flex-direction: column;
101 | margin-right: auto;
102 |
103 | strong {
104 | font-style: normal;
105 | font-weight: 600;
106 | font-size: 20px;
107 | line-height: 24px;
108 | color: ${({theme}) => theme.colors.titleTimer };
109 | }
110 |
111 | div{
112 | display: flex;
113 | align-items: flex-start;
114 | justify-content: flex-start;
115 | margin-top: 8px;
116 |
117 | img{
118 | margin-right: 10px
119 | }
120 |
121 | span{
122 | font-style: normal;
123 | font-weight: normal;
124 | font-size: 1rem;
125 | line-height: 19px;
126 | }
127 | }
128 |
129 | }
130 | }
131 | }
132 | }
133 | }
134 |
135 | @media (max-width: 800px){
136 | margin: 0 10px;
137 | padding-left: 0px;
138 | overflow-x: scroll;
139 | }
140 | }
141 | `;
--------------------------------------------------------------------------------
/src/styles/theme.ts:
--------------------------------------------------------------------------------
1 | export const light = {
2 | title: 'light',
3 | colors: {
4 | white: '#fff',
5 | background: '#f2f3f5',
6 | grayLine: '#dcdde0',
7 | text: '#666666',
8 | textHighlight: '#b3b9ff',
9 | title: '#2e384d',
10 | red: '#e83f5b',
11 | green: '#4cd62b',
12 | blue: '#5965e0',
13 | blueDark: '#4953b8',
14 | blueTwitter: '#2aa9e0',
15 |
16 | backgroundModal: 'rgba(242, 243, 245, 0.8)',
17 | backgroundModalContent: '#fff',
18 | backgroundIndex: '#5964e0',
19 | backgroundLight: '#fff',
20 | titleTimer: '#2e384d',
21 | textDark: '#666666',
22 | titleButton: '#2e384d',
23 | titleLight: '#848FFF',
24 | titleLighten: '#B2B9FF',
25 | logoColor: '#5965e0',
26 | inputBackground: '#4953b8',
27 | inputBackgroundFinish: 'rgba(73, 83, 184, 0.2)',
28 | hightLightTable: '#5965E0',
29 | }
30 | }
31 |
32 | export const dark = {
33 | title: 'dark',
34 | colors: {
35 | white: '#fff',
36 | background: '#0B1529',
37 | grayLine: '#dcdde0',
38 | text: '#FFFFFF',
39 | textHighlight: '#b3b9ff',
40 | title: '#5965E0',
41 | red: '#e83f5b',
42 | green: '#4cd62b',
43 | blue: '#5965e0',
44 | blueDark: '#4953b8',
45 | blueTwitter: '#2aa9e0',
46 |
47 | backgroundModal: 'rgba(11, 21, 41, 0.7)',
48 | backgroundModalContent: '#0B1529',
49 | backgroundIndex: '#0B1529',
50 | backgroundLight: '#273248',
51 | titleTimer: '#dcdde0',
52 | textDark: '#666666',
53 | titleButton: '#dcdde0',
54 | titleLight: '#848FFF',
55 | titleLighten: '#B2B9FF',
56 | logoColor: '#fff',
57 | inputBackground: 'rgba( 255, 255, 255, 0.45)',
58 | inputBackgroundFinish: 'transparent',
59 | hightLightTable: '#929eaa',
60 | },
61 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": false,
12 | "forceConsistentCasingInFileNames": true,
13 | "noEmit": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "jsx": "preserve"
20 | },
21 | "include": [
22 | "next-env.d.ts",
23 | "**/*.ts",
24 | "**/*.tsx"
25 | ],
26 | "exclude": [
27 | "node_modules"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------