├── moveit ├── next-env.d.ts ├── public │ ├── favicon.png │ ├── notification.mp3 │ ├── icons │ │ ├── close.svg │ │ ├── twitter.svg │ │ ├── eye.svg │ │ ├── level.svg │ │ ├── level-up.svg │ │ ├── levelup.svg │ │ └── body.svg │ └── logo-full.svg ├── src │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ └── index.tsx │ ├── styles │ │ ├── pages │ │ │ └── Home.module.css │ │ ├── components │ │ │ ├── CompletedChallenges.module.css │ │ │ ├── Profile.module.css │ │ │ ├── ExperienceBar.module.css │ │ │ ├── LevelUpModal.module.css │ │ │ ├── Countdown.module.css │ │ │ └── ChallengeBox.module.css │ │ └── global.css │ ├── components │ │ ├── CompletedChallenges.tsx │ │ ├── Profile.tsx │ │ ├── LevelUpModal.tsx │ │ ├── ExperienceBar.tsx │ │ ├── ChallengeBox.tsx │ │ └── Countdown.tsx │ └── context │ │ ├── CountdownContext.tsx │ │ └── ChallengesContext.tsx ├── package.json ├── tsconfig.json ├── README.md └── challenges.json ├── rocketpay ├── priv │ ├── repo │ │ ├── migrations │ │ │ ├── .formatter.exs │ │ │ ├── 20210224114146_create_accounts_table.exs │ │ │ └── 20210223144459_create_user_table.exs │ │ └── seeds.exs │ └── gettext │ │ ├── en │ │ └── LC_MESSAGES │ │ │ └── errors.po │ │ └── errors.pot ├── test │ ├── test_helper.exs │ ├── rocketpay_web │ │ ├── views │ │ │ ├── error_view_test.exs │ │ │ └── users_view_test.exs │ │ └── controllers │ │ │ └── accounts_controller_test.exs │ ├── rocketpay │ │ ├── numbers_test.exs │ │ └── users │ │ │ └── create_test.exs │ └── support │ │ ├── channel_case.ex │ │ ├── conn_case.ex │ │ └── data_case.ex ├── lib │ ├── rocketpay │ │ ├── repo.ex │ │ ├── accounts │ │ │ ├── transactions │ │ │ │ └── response.ex │ │ │ ├── deposit.ex │ │ │ ├── withdraw.ex │ │ │ ├── transaction.ex │ │ │ └── operation.ex │ │ ├── account.ex │ │ ├── users │ │ │ └── create.ex │ │ ├── application.ex │ │ └── user.ex │ ├── rocketpay_web │ │ ├── controllers │ │ │ ├── fallback_controller.ex │ │ │ ├── users_controller.ex │ │ │ ├── welcome_controller.ex │ │ │ └── accounts_controller.ex │ │ ├── views │ │ │ ├── users_view.ex │ │ │ ├── error_view.ex │ │ │ ├── accounts_view.ex │ │ │ └── error_helpers.ex │ │ ├── gettext.ex │ │ ├── channels │ │ │ └── user_socket.ex │ │ ├── router.ex │ │ ├── endpoint.ex │ │ └── telemetry.ex │ ├── rocketpay.ex │ └── rocketpay_web.ex ├── .formatter.exs ├── coveralls.json ├── .gitignore ├── config │ ├── test.exs │ ├── config.exs │ ├── dev.exs │ └── prod.exs ├── README.md ├── mix.exs ├── .credo.exs └── mix.lock ├── npsapi ├── src │ ├── server.ts │ ├── repositories │ │ ├── UserRepository.ts │ │ ├── SurveyRepository.ts │ │ └── SurveysUserRepository.ts │ ├── errors │ │ └── AppError.ts │ ├── database │ │ ├── index.ts │ │ └── migrations │ │ │ ├── 1614079873737-CreateUsers.ts │ │ │ ├── 1614165266371-CreateSurveys.ts │ │ │ └── 1614251053639-CreateSurveysUsers.ts │ ├── models │ │ ├── User.ts │ │ ├── Survey.ts │ │ └── SurveyUser.ts │ ├── __tests__ │ │ ├── Answer.test.ts │ │ ├── User.test.ts │ │ └── Survey.test.ts │ ├── app.ts │ ├── controllers │ │ ├── AnswerController.ts │ │ ├── UserController.ts │ │ ├── SurveyController.ts │ │ ├── NpsController.ts │ │ └── SendMailController.ts │ ├── routes.ts │ ├── services │ │ └── SendMailService.ts │ └── views │ │ └── emails │ │ └── npsMail.hbs ├── ormconfig.json ├── package.json ├── README.md ├── tsconfig.json └── jest.config.ts ├── .editorconfig ├── README.md ├── LICENSE └── .gitignore /moveit/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /moveit/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3tr074/nlw4-monorepo/HEAD/moveit/public/favicon.png -------------------------------------------------------------------------------- /rocketpay/priv/repo/migrations/.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | import_deps: [:ecto_sql], 3 | inputs: ["*.exs"] 4 | ] 5 | -------------------------------------------------------------------------------- /rocketpay/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, :manual) 3 | -------------------------------------------------------------------------------- /moveit/public/notification.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3tr074/nlw4-monorepo/HEAD/moveit/public/notification.mp3 -------------------------------------------------------------------------------- /npsapi/src/server.ts: -------------------------------------------------------------------------------- 1 | import { app } from "./app"; 2 | 3 | app.listen(3000, () => console.log("Server is running!")); 4 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/repo.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Repo do 2 | use Ecto.Repo, 3 | otp_app: :rocketpay, 4 | adapter: Ecto.Adapters.Postgres 5 | end 6 | -------------------------------------------------------------------------------- /moveit/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "@styles/global.css"; 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return ; 5 | } 6 | 7 | export default MyApp; 8 | -------------------------------------------------------------------------------- /rocketpay/.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | import_deps: [:ecto, :phoenix], 3 | inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"], 4 | subdirectories: ["priv/*/migrations"] 5 | ] 6 | -------------------------------------------------------------------------------- /npsapi/src/repositories/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from "typeorm"; 2 | import { User } from "../models/User"; 3 | 4 | @EntityRepository(User) 5 | class UsersRepository extends Repository {} 6 | 7 | export { UsersRepository }; 8 | -------------------------------------------------------------------------------- /npsapi/src/repositories/SurveyRepository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from "typeorm"; 2 | import { Survey } from "../models/Survey"; 3 | 4 | @EntityRepository(Survey) 5 | class SurveysRepository extends Repository {} 6 | 7 | export { SurveysRepository }; 8 | -------------------------------------------------------------------------------- /moveit/public/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /npsapi/src/errors/AppError.ts: -------------------------------------------------------------------------------- 1 | export class AppError { 2 | public readonly message: string; 3 | public readonly statusCode: number; 4 | 5 | constructor(message: string, statusCode = 400) { 6 | this.message = message; 7 | this.statusCode = statusCode; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /npsapi/ormconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "sqlite", 3 | "database": "./src/database/database.sqlite", 4 | "migrations": ["./src/database/migrations/**.ts"], 5 | "entities": ["./src/models/**.ts"], 6 | "cli": { 7 | "migrationsDir": "./src/database/migrations" 8 | } 9 | } -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /npsapi/src/repositories/SurveysUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from "typeorm"; 2 | import { SurveyUser } from "../models/SurveyUser"; 3 | 4 | @EntityRepository(SurveyUser) 5 | class SurveysUsersRepository extends Repository {} 6 | 7 | export { SurveysUsersRepository }; 8 | -------------------------------------------------------------------------------- /rocketpay/coveralls.json: -------------------------------------------------------------------------------- 1 | { 2 | "skip_files": [ 3 | "test/", 4 | "lib/rocketpay_web/channels/user_socket.ex", 5 | "lib/rocketpay_web/endpoint.ex", 6 | "lib/rocketpay_web/gettext.ex", 7 | "lib/rocketpay_web/telemetry.ex" 8 | ], 9 | "coverage_options": { 10 | "treat_no_relevant_lines_as_covered": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/controllers/fallback_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.FallbackController do 2 | use RocketpayWeb, :controller 3 | 4 | def call(conn, {:error, result}) do 5 | conn 6 | |> put_status(:bad_request) 7 | |> put_view(RocketpayWeb.ErrorView) 8 | |> render("400.json", result: result) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /moveit/src/styles/pages/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 100vh; 3 | max-width: 992px; 4 | margin: 0 auto; 5 | padding: 2.5rem 2rem; 6 | 7 | display: flex; 8 | flex-direction: column; 9 | } 10 | 11 | .container section { 12 | flex: 1; 13 | 14 | display: grid; 15 | grid-template-columns: 1fr 1fr; 16 | gap: 6.25rem; 17 | align-content: center; 18 | } 19 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/accounts/transactions/response.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Accounts.Transactions.Response do 2 | alias Rocketpay.Account 3 | defstruct [:from_account, :to_account] 4 | 5 | def build(%Account{} = from_account, %Account{} = to_account) do 6 | %__MODULE__{ 7 | from_account: from_account, 8 | to_account: to_account 9 | } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /rocketpay/priv/repo/seeds.exs: -------------------------------------------------------------------------------- 1 | # Script for populating the database. You can run it as: 2 | # 3 | # mix run priv/repo/seeds.exs 4 | # 5 | # Inside the script, you can read and write to any of your 6 | # repositories directly: 7 | # 8 | # Rocketpay.Repo.insert!(%Rocketpay.SomeSchema{}) 9 | # 10 | # We recommend using the bang functions (`insert!`, `update!` 11 | # and so on) as they will fail if something goes wrong. 12 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay do 2 | alias Rocketpay.Users.Create, as: UserCreate 3 | 4 | alias Rocketpay.Accounts.{Deposit, Transaction, Withdraw} 5 | 6 | defdelegate create_user(params), to: UserCreate, as: :call 7 | 8 | defdelegate deposit_value(params), to: Deposit, as: :call 9 | defdelegate withdraw_value(params), to: Withdraw, as: :call 10 | defdelegate transaction(params), to: Transaction, as: :call 11 | end 12 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/controllers/users_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.UsersController do 2 | use RocketpayWeb, :controller 3 | 4 | alias Rocketpay.User 5 | 6 | action_fallback RocketpayWeb.FallbackController 7 | 8 | def create(conn, params) do 9 | with {:ok, %User{} = user} <- Rocketpay.create_user(params) do 10 | conn 11 | |> put_status(:created) 12 | |> render("create.json", user: user) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /rocketpay/priv/repo/migrations/20210224114146_create_accounts_table.exs: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Repo.Migrations.CreateAccountsTable do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:accounts) do 6 | add :balance, :decimal 7 | add :user_id, references(:users, type: :binary_id) 8 | 9 | timestamps() 10 | end 11 | 12 | create constraint(:accounts, :balance_must_be_positive_or_zero, check: "balance >= 0") 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /npsapi/src/database/index.ts: -------------------------------------------------------------------------------- 1 | import { Connection, createConnection, getConnectionOptions } from "typeorm"; 2 | 3 | export default async (): Promise => { 4 | const defaultOptions = await getConnectionOptions(); 5 | return createConnection( 6 | Object.assign(defaultOptions, { 7 | database: 8 | process.env.NODE_ENV === "test" 9 | ? "./src/database/database.test.sqlite" 10 | : defaultOptions.database, 11 | }) 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /npsapi/src/models/User.ts: -------------------------------------------------------------------------------- 1 | import { Column, CreateDateColumn, Entity, PrimaryColumn } from "typeorm"; 2 | import { v4 as uuid } from "uuid"; 3 | 4 | @Entity("users") 5 | class User { 6 | @PrimaryColumn() 7 | readonly id: string; 8 | 9 | @Column() 10 | name: string; 11 | 12 | @Column() 13 | email: string; 14 | 15 | @CreateDateColumn() 16 | created_at: Date; 17 | 18 | constructor() { 19 | if (!this.id) { 20 | this.id = uuid(); 21 | } 22 | } 23 | } 24 | 25 | export { User }; 26 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/accounts/deposit.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Accounts.Deposit do 2 | alias Rocketpay.Accounts.Operation 3 | alias Rocketpay.Repo 4 | 5 | def call(params) do 6 | params 7 | |> Operation.call(:deposit) 8 | |> run_transaction() 9 | end 10 | 11 | defp run_transaction(multi) do 12 | case Repo.transaction(multi) do 13 | {:error, _operation, reason, _changes} -> {:error, reason} 14 | {:ok, %{deposit: account}} -> {:ok, account} 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/accounts/withdraw.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Accounts.Withdraw do 2 | alias Rocketpay.Accounts.Operation 3 | alias Rocketpay.Repo 4 | 5 | def call(params) do 6 | params 7 | |> Operation.call(:withdraw) 8 | |> run_transaction() 9 | end 10 | 11 | defp run_transaction(multi) do 12 | case Repo.transaction(multi) do 13 | {:error, _operation, reason, _changes} -> {:error, reason} 14 | {:ok, %{withdraw: account}} -> {:ok, account} 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /npsapi/src/models/Survey.ts: -------------------------------------------------------------------------------- 1 | import { Column, CreateDateColumn, Entity, PrimaryColumn } from "typeorm"; 2 | import { v4 as uuid } from "uuid"; 3 | 4 | @Entity("surveys") 5 | class Survey { 6 | @PrimaryColumn() 7 | readonly id: string; 8 | 9 | @Column() 10 | title: string; 11 | 12 | @Column() 13 | description: string; 14 | 15 | @CreateDateColumn() 16 | created_at: Date; 17 | 18 | constructor() { 19 | if (!this.id) { 20 | this.id = uuid(); 21 | } 22 | } 23 | } 24 | 25 | export { Survey }; 26 | -------------------------------------------------------------------------------- /rocketpay/priv/repo/migrations/20210223144459_create_user_table.exs: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Repo.Migrations.CreateUserTable do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:users) do 6 | add :name, :string 7 | add :age, :integer 8 | add :email, :string 9 | add :password_hash, :string 10 | add :nickname, :string 11 | 12 | timestamps() 13 | end 14 | 15 | create unique_index(:users, [:email]) 16 | create unique_index(:users, [:nickname]) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /moveit/src/styles/components/CompletedChallenges.module.css: -------------------------------------------------------------------------------- 1 | .completedChallengesContainer { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | 6 | margin: 3.5rem 0; 7 | padding-bottom: 1rem; 8 | border-bottom: 1px solid #d7d8da; 9 | 10 | font-weight: 500; 11 | } 12 | 13 | .completedChallengesContainer svg { 14 | margin-right: 1rem; 15 | } 16 | 17 | .completedChallengesContainer span:first-child { 18 | font-size: 1.25rem; 19 | } 20 | 21 | .completedChallengesContainer span:last-child { 22 | font-size: 1.5rem; 23 | } 24 | -------------------------------------------------------------------------------- /rocketpay/test/rocketpay_web/views/error_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.ErrorViewTest do 2 | use RocketpayWeb.ConnCase, async: true 3 | 4 | # Bring render/3 and render_to_string/3 for testing custom views 5 | import Phoenix.View 6 | 7 | test "renders 404.json" do 8 | assert render(RocketpayWeb.ErrorView, "404.json", []) == %{errors: %{detail: "Not Found"}} 9 | end 10 | 11 | test "renders 500.json" do 12 | assert render(RocketpayWeb.ErrorView, "500.json", []) == 13 | %{errors: %{detail: "Internal Server Error"}} 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /moveit/src/styles/components/Profile.module.css: -------------------------------------------------------------------------------- 1 | .profileContainer { 2 | display: flex; 3 | align-items: center; 4 | } 5 | 6 | .profileContainer > img { 7 | width: 5.5rem; 8 | height: 5.5rem; 9 | border-radius: 50%; 10 | } 11 | 12 | .profileContainer div { 13 | margin-left: 1.5rem; 14 | } 15 | 16 | .profileContainer div strong { 17 | font-size: 1.5rem; 18 | font-weight: 600; 19 | color: var(--title); 20 | } 21 | 22 | .profileContainer div p { 23 | font-size: 1rem; 24 | margin-top: 0.5rem; 25 | } 26 | 27 | .profileContainer div p img { 28 | margin-right: 0.5rem; 29 | } 30 | -------------------------------------------------------------------------------- /moveit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nlw4-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "js-cookie": "^2.2.1", 12 | "next": "10.0.7", 13 | "react": "17.0.1", 14 | "react-dom": "17.0.1", 15 | "react-icons": "^4.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/js-cookie": "^2.2.6", 19 | "@types/node": "^14.14.31", 20 | "@types/react": "^17.0.2", 21 | "@types/react-dom": "^17.0.1", 22 | "typescript": "^4.2.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /moveit/src/styles/components/ExperienceBar.module.css: -------------------------------------------------------------------------------- 1 | .experienceBar { 2 | display: flex; 3 | align-items: center; 4 | } 5 | 6 | .experienceBar span { 7 | font-size: 1rem; 8 | } 9 | 10 | .experienceBar > div { 11 | flex: 1; 12 | height: 4px; 13 | border-radius: 4px; 14 | background: var(--gray-line); 15 | margin: 0 1.5rem; 16 | position: relative; 17 | } 18 | 19 | .experienceBar > div > div { 20 | height: 4px; 21 | border-radius: 4px; 22 | background: var(--green); 23 | } 24 | 25 | span.currentExperience { 26 | position: absolute; 27 | top: 12px; 28 | transform: translateX(-50%); 29 | } 30 | -------------------------------------------------------------------------------- /moveit/src/components/CompletedChallenges.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import styles from "@styles/components/CompletedChallenges.module.css"; 3 | import { ChallengeContext } from "src/context/ChallengesContext"; 4 | import { FiAward } from "react-icons/fi"; 5 | 6 | export const CompletedChallenges: React.FC = () => { 7 | const { challengesCompleted } = useContext(ChallengeContext); 8 | 9 | return ( 10 |
11 | 12 | 13 | Desafios Completos 14 | 15 | 16 | {challengesCompleted} 17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /moveit/src/components/Profile.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import styles from "@styles/components/Profile.module.css"; 3 | import { ChallengeContext } from "src/context/ChallengesContext"; 4 | 5 | export const Profile: React.FC = () => { 6 | const { level } = useContext(ChallengeContext); 7 | 8 | return ( 9 |
10 | R3tr0 11 |
12 | Jorge Buzeti 13 |

14 | Level 15 | Level {level} 16 |

17 |
18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/controllers/welcome_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.WelcomeController do 2 | use RocketpayWeb, :controller 3 | 4 | alias Rocketpay.Numbers 5 | 6 | def index(conn, %{"filename" => filename}) do 7 | filename 8 | |> Numbers.sum_from_file() 9 | |> handle_response(conn) 10 | end 11 | 12 | defp handle_response({:ok, %{result: result}}, conn) do 13 | conn 14 | |> put_status(:ok) 15 | |> json(%{message: "Welcome to Rocketpay API; Here is your number #{result}"}) 16 | end 17 | 18 | defp handle_response({:error, reason}, conn) do 19 | conn 20 | |> put_status(:bad_request) 21 | |> json(reason) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /rocketpay/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,elixir 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,elixir 4 | 5 | ### Elixir ### 6 | /_build 7 | /cover 8 | /deps 9 | /doc 10 | /.fetch 11 | erl_crash.dump 12 | *.ez 13 | *.beam 14 | /config/*.secret.exs 15 | .elixir_ls/ 16 | 17 | ### Elixir Patch ### 18 | 19 | ### VisualStudioCode ### 20 | .vscode/* 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | *.code-workspace 24 | 25 | ### VisualStudioCode Patch ### 26 | # Ignore all local history of files 27 | .history 28 | .ionide 29 | 30 | # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,elixir 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is a monoRepo with all NLW 4 projects 2 | 3 | ## Projects: 4 | 5 | 1. [Move It](/moveit), the React project 6 | 2. [Rocketpay](/rocketpay), the Elixir project 7 | 3. [nps api](/npsapi), the node project 8 | 9 | special thanks for the amazing [rocketseat](https://github.com/rocketseat) event, the [Next Level Weak](https://nextlevelweek.com/) 10 | 11 | ## 💻 Author 12 | 13 | Author avatar 14 | 15 | Write by Jorge Buzeti: 16 | [get in touch](https://github.com/R3tr074#-get-in-touch) 17 | 18 | ## License 19 | 20 | Distributed under the MIT License. See [`LICENSE`](/LICENSE) for more information. 21 | -------------------------------------------------------------------------------- /moveit/public/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /moveit/src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | export default class MyDocument extends Document { 4 | render() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /rocketpay/test/rocketpay/numbers_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.NumbersTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Rocketpay.Numbers 5 | 6 | describe "sum_from_file/1" do 7 | test "when there is a file with the given name, returns the sum of numbers" do 8 | response = Numbers.sum_from_file("numbers") 9 | 10 | expected_response = {:ok, %{result: 37}} 11 | 12 | assert response == expected_response 13 | end 14 | 15 | test "when there is no file with the given name, returns an error" do 16 | response = Numbers.sum_from_file("banana") 17 | 18 | expected_response = {:error, %{message: "Invalid file"}} 19 | 20 | assert response == expected_response 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /moveit/src/components/LevelUpModal.tsx: -------------------------------------------------------------------------------- 1 | import styles from "@styles/components/LevelUpModal.module.css"; 2 | import { useContext } from "react"; 3 | import { ChallengeContext } from "~/context/ChallengesContext"; 4 | 5 | export function LevelUpModal() { 6 | const { level, closeLevelUpModal } = useContext(ChallengeContext); 7 | 8 | return ( 9 |
10 |
11 |
{level}
12 | 13 | Parabéns 14 |

Você alcançou um novo level.

15 | 16 | 19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /npsapi/src/__tests__/Answer.test.ts: -------------------------------------------------------------------------------- 1 | import request from "supertest"; 2 | import { createConnection, getConnection } from "typeorm"; 3 | import { app } from "../app"; 4 | 5 | describe("Answer", () => { 6 | beforeAll(async () => { 7 | const connection = await createConnection(); 8 | await connection.runMigrations(); 9 | }); 10 | 11 | afterAll(async () => { 12 | const connection = getConnection(); 13 | await connection.dropDatabase(); 14 | await connection.close(); 15 | }); 16 | 17 | it("Deve retornar erro para pesquisas inexistentes", async () => { 18 | const response = await request(app).get("/answers/10?u=123"); 19 | 20 | expect(response.status).toBe(400); 21 | expect(response.body).toHaveProperty("message"); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/account.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Account do 2 | use Ecto.Schema 3 | import Ecto.Changeset 4 | 5 | alias Rocketpay.User 6 | 7 | @primary_key {:id, :binary_id, autogenerate: true} 8 | @foreign_key_type :binary_id 9 | 10 | @required_params [:balance, :user_id] 11 | 12 | schema "accounts" do 13 | field :balance, :decimal 14 | belongs_to :user, User 15 | 16 | timestamps() 17 | end 18 | 19 | def changeset(struct \\ %__MODULE__{}, params) do 20 | struct 21 | |> cast(params, @required_params) 22 | |> validate_required(@required_params) 23 | |> check_constraint(:balance, 24 | name: :balance_must_be_positive_or_zero, 25 | message: "Account value cannot be negative" 26 | ) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /npsapi/src/app.ts: -------------------------------------------------------------------------------- 1 | import express, { NextFunction, Request, Response } from "express"; 2 | import "express-async-errors"; 3 | import "reflect-metadata"; 4 | import createConnection from "./database"; 5 | import { AppError } from "./errors/AppError"; 6 | import { router } from "./routes"; 7 | createConnection(); 8 | 9 | const app = express(); 10 | 11 | app.use(express.json()); 12 | app.use(router); 13 | app.use((err: Error, req: Request, res: Response, _next: NextFunction) => { 14 | if (err instanceof AppError) { 15 | return res.status(err.statusCode).json({ 16 | message: err.message, 17 | }); 18 | } 19 | 20 | return res.status(500).json({ 21 | status: "Error", 22 | message: `Internal server erro ${err.message}`, 23 | }); 24 | }); 25 | 26 | export { app }; 27 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/views/users_view.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.UsersView do 2 | use RocketpayWeb, :view 3 | 4 | alias Rocketpay.{Account, User} 5 | 6 | def render( 7 | "create.json", 8 | %{ 9 | user: %User{ 10 | id: id, 11 | name: name, 12 | nickname: nickname, 13 | account: %Account{ 14 | id: account_id, 15 | balance: balance 16 | } 17 | } 18 | } 19 | ) do 20 | %{ 21 | message: "User created", 22 | user: %{ 23 | id: id, 24 | name: name, 25 | nickname: nickname, 26 | account: %{ 27 | id: account_id, 28 | balance: balance 29 | } 30 | } 31 | } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /moveit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": false, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "baseUrl": ".", 17 | "paths": { 18 | "@styles/*": ["./src/styles/*"], 19 | "@components/*": ["./src/components/*"], 20 | "@context/*": ["./src/context/*"], 21 | "~/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /rocketpay/config/test.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # Configure your database 4 | # 5 | # The MIX_TEST_PARTITION environment variable can be used 6 | # to provide built-in test partitioning in CI environment. 7 | # Run `mix help test` for more information. 8 | config :rocketpay, Rocketpay.Repo, 9 | username: "postgres", 10 | password: "postgres", 11 | database: "rocketpay_test#{System.get_env("MIX_TEST_PARTITION")}", 12 | hostname: System.get_env("DB_HOST", "localhost"), 13 | pool: Ecto.Adapters.SQL.Sandbox 14 | 15 | # We don't run a server during test. If one is required, 16 | # you can enable the server option below. 17 | config :rocketpay, RocketpayWeb.Endpoint, 18 | http: [port: 4002], 19 | server: false 20 | 21 | # Print only warnings and errors during test 22 | config :logger, level: :warn 23 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/gettext.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.Gettext do 2 | @moduledoc """ 3 | A module providing Internationalization with a gettext-based API. 4 | 5 | By using [Gettext](https://hexdocs.pm/gettext), 6 | your module gains a set of macros for translations, for example: 7 | 8 | import RocketpayWeb.Gettext 9 | 10 | # Simple translation 11 | gettext("Here is the string to translate") 12 | 13 | # Plural translation 14 | ngettext("Here is the string to translate", 15 | "Here are the strings to translate", 16 | 3) 17 | 18 | # Domain-based translation 19 | dgettext("errors", "Here is the error message to translate") 20 | 21 | See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. 22 | """ 23 | use Gettext, otp_app: :rocketpay 24 | end 25 | -------------------------------------------------------------------------------- /npsapi/src/__tests__/User.test.ts: -------------------------------------------------------------------------------- 1 | import request from "supertest"; 2 | import { app } from "../app"; 3 | 4 | import createConnection from "../database"; 5 | 6 | describe("Users", () => { 7 | beforeAll(async () => { 8 | const connection = await createConnection(); 9 | await connection.runMigrations(); 10 | }); 11 | 12 | it("Should be create a new user", async () => { 13 | const response = await request(app).post("/users").send({ 14 | email: "user@example.com", 15 | name: "User Example", 16 | }); 17 | 18 | expect(response.status).toBe(201); 19 | }); 20 | 21 | it("Should not be able to create a user with exists email", async () => { 22 | const response = await request(app).post("/users").send({ 23 | email: "user@example.com", 24 | name: "User Example", 25 | }); 26 | 27 | expect(response.status).toBe(400); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /npsapi/src/controllers/AnswerController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getCustomRepository } from "typeorm"; 3 | import { SurveysUsersRepository } from "../repositories/SurveysUserRepository"; 4 | import { AppError } from "../errors/AppError"; 5 | 6 | class AnswerController { 7 | async execute(req: Request, res: Response) { 8 | const { value } = req.params; 9 | const { u } = req.query; 10 | 11 | const surveysUsersRepository = getCustomRepository(SurveysUsersRepository); 12 | const surveyUser = await surveysUsersRepository.findOne({ 13 | id: String(u), 14 | }); 15 | 16 | if (!surveyUser) { 17 | throw new AppError("Survey user not found"); 18 | } 19 | 20 | surveyUser.value = Number(value); 21 | await surveysUsersRepository.save(surveyUser); 22 | 23 | return res.json(surveyUser); 24 | } 25 | } 26 | 27 | export { AnswerController }; 28 | -------------------------------------------------------------------------------- /npsapi/src/models/SurveyUser.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Column, 3 | CreateDateColumn, 4 | Entity, 5 | JoinColumn, 6 | ManyToOne, 7 | PrimaryColumn, 8 | } from "typeorm"; 9 | import { v4 as uuid } from "uuid"; 10 | import { Survey } from "./Survey"; 11 | import { User } from "./User"; 12 | 13 | @Entity("surveys_users") 14 | class SurveyUser { 15 | @PrimaryColumn() 16 | readonly id: string; 17 | 18 | @Column() 19 | user_id: string; 20 | 21 | @ManyToOne(() => User) 22 | @JoinColumn({ name: "user_id" }) 23 | user: User; 24 | 25 | @Column() 26 | survey_id: string; 27 | 28 | @ManyToOne(() => Survey) 29 | @JoinColumn({ name: "survey_id" }) 30 | survey: Survey; 31 | 32 | @Column() 33 | value: number; 34 | 35 | @CreateDateColumn() 36 | created_at: Date; 37 | 38 | constructor() { 39 | if (!this.id) { 40 | this.id = uuid(); 41 | } 42 | } 43 | } 44 | 45 | export { SurveyUser }; 46 | -------------------------------------------------------------------------------- /moveit/src/components/ExperienceBar.tsx: -------------------------------------------------------------------------------- 1 | import styles from "@styles/components/ExperienceBar.module.css"; 2 | import { useContext } from "react"; 3 | import { ChallengeContext } from "src/context/ChallengesContext"; 4 | 5 | export function ExperienceBar() { 6 | const { currentExperience, experienceToNextLevel } = useContext( 7 | ChallengeContext 8 | ); 9 | 10 | const percentToNextLevel = 11 | Math.round(currentExperience * 100) / experienceToNextLevel; 12 | 13 | return ( 14 |
15 | 0 xp 16 |
17 |
18 | 22 | {currentExperience} xp 23 | 24 |
25 | {experienceToNextLevel} xp 26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /rocketpay/test/rocketpay_web/views/users_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.UsersViewTest do 2 | use RocketpayWeb.ConnCase, async: true 3 | 4 | import Phoenix.View 5 | 6 | alias Rocketpay.{Account, User} 7 | alias RocketpayWeb.UsersView 8 | 9 | test "render create.json" do 10 | params = %{ 11 | name: "FakeName", 12 | password: "FakePassword", 13 | nickname: "FakeNickname", 14 | email: "Fake@email.com", 15 | age: 31 16 | } 17 | 18 | {:ok, %User{id: user_id, account: %Account{id: account_id}} = user} = 19 | Rocketpay.create_user(params) 20 | 21 | response = render(UsersView, "create.json", user: user) 22 | 23 | expected_response = %{ 24 | message: "User created", 25 | user: %{ 26 | account: %{balance: Decimal.new("0.00"), id: account_id}, 27 | id: user_id, 28 | name: "FakeName", 29 | nickname: "FakeNickname" 30 | } 31 | } 32 | 33 | assert response == expected_response 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /npsapi/src/database/migrations/1614079873737-CreateUsers.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner, Table } from "typeorm"; 2 | 3 | export class CreateUsers1614079873737 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.createTable( 6 | new Table({ 7 | name: "users", 8 | columns: [ 9 | { 10 | name: "id", 11 | type: "uuid", 12 | isPrimary: true, 13 | }, 14 | { 15 | name: "name", 16 | type: "varchar", 17 | }, 18 | { 19 | name: "email", 20 | type: "varchar", 21 | }, 22 | { 23 | name: "created_at", 24 | type: "timestamp", 25 | default: "now()", 26 | }, 27 | ], 28 | }) 29 | ); 30 | } 31 | 32 | public async down(queryRunner: QueryRunner): Promise { 33 | await queryRunner.dropTable("users"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /npsapi/src/database/migrations/1614165266371-CreateSurveys.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner, Table } from "typeorm"; 2 | 3 | export class CreateSurveys1614165266371 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.createTable( 6 | new Table({ 7 | name: "surveys", 8 | columns: [ 9 | { 10 | name: "id", 11 | type: "uuid", 12 | isPrimary: true, 13 | }, 14 | { 15 | name: "title", 16 | type: "varchar", 17 | }, 18 | { 19 | name: "description", 20 | type: "varchar", 21 | }, 22 | { 23 | name: "created_at", 24 | type: "timestamp", 25 | default: "now()", 26 | }, 27 | ], 28 | }) 29 | ); 30 | } 31 | 32 | public async down(queryRunner: QueryRunner): Promise { 33 | await queryRunner.dropTable("surveys"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /npsapi/src/routes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { AnswerController } from "./controllers/AnswerController"; 3 | import { NpsController } from "./controllers/NpsController"; 4 | import { SendMailController } from "./controllers/SendMailController"; 5 | import { SurveyController } from "./controllers/SurveyController"; 6 | import { UserController } from "./controllers/UserController"; 7 | 8 | const router = Router(); 9 | const userController = new UserController(); 10 | const surveysController = new SurveyController(); 11 | const sendMailController = new SendMailController(); 12 | const answerController = new AnswerController(); 13 | const npsController = new NpsController(); 14 | 15 | router.post("/users", userController.create); 16 | router.post("/surveys", surveysController.create); 17 | router.get("/surveys", surveysController.show); 18 | router.post("/sendMail", sendMailController.execute); 19 | router.get("/answers/:value", answerController.execute); 20 | router.get("/nps/:survey_id", npsController.execute); 21 | 22 | export { router }; 23 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/controllers/accounts_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.AccountsController do 2 | use RocketpayWeb, :controller 3 | 4 | alias Rocketpay.Account 5 | 6 | alias Rocketpay.Accounts.Transactions.Response, as: TransactionResponse 7 | 8 | action_fallback RocketpayWeb.FallbackController 9 | 10 | def deposit(conn, params) do 11 | with {:ok, %Account{} = account} <- Rocketpay.deposit_value(params) do 12 | conn 13 | |> put_status(:ok) 14 | |> render("update.json", account: account) 15 | end 16 | end 17 | 18 | def withdraw(conn, params) do 19 | with {:ok, %Account{} = account} <- Rocketpay.withdraw_value(params) do 20 | conn 21 | |> put_status(:ok) 22 | |> render("update.json", account: account) 23 | end 24 | end 25 | 26 | def transaction(conn, params) do 27 | with {:ok, %TransactionResponse{} = transaction} <- Rocketpay.transaction(params) do 28 | conn 29 | |> put_status(:ok) 30 | |> render("transaction.json", transaction: transaction) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /moveit/src/styles/global.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --white: #fff; 3 | --background: #f2f3f5; 4 | --gray-line: #dcdde0; 5 | --text: #666666; 6 | --text-highlight: #b3b9ff; 7 | --title: #2e384d; 8 | --red: #e83f5b; 9 | --green: #4cd62b; 10 | --blue: #5965e0; 11 | --blue-dark: #4953b8; 12 | --blue-twitter: #2aa9e0; 13 | } 14 | 15 | * { 16 | margin: 0; 17 | padding: 0; 18 | box-sizing: border-box; 19 | } 20 | 21 | body { 22 | background: var(--background); 23 | color: var(--text); 24 | } 25 | 26 | body, 27 | input, 28 | textarea, 29 | button { 30 | font: 400 1rem "Inter", sans-serif; 31 | } 32 | 33 | button { 34 | cursor: pointer; 35 | } 36 | 37 | a { 38 | color: inherit; 39 | text-decoration: none; 40 | } 41 | 42 | .container { 43 | height: 100vh; 44 | max-width: 992px; 45 | margin: 0 auto; 46 | padding: 2.5rem 2rem; 47 | display: flex; 48 | flex-direction: column; 49 | } 50 | 51 | @media (max-width: 1080px) { 52 | html { 53 | font-size: 93.75%; 54 | } 55 | } 56 | 57 | @media (max-width: 720px) { 58 | html { 59 | font-size: 87.5%; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/accounts/transaction.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Accounts.Transaction do 2 | alias Ecto.Multi 3 | 4 | alias Rocketpay.Repo 5 | 6 | alias Rocketpay.Accounts.Operation 7 | 8 | alias Rocketpay.Accounts.Transactions.Response, as: TransactionResponse 9 | 10 | def call(%{"from" => from_id, "to" => to_id, "value" => value}) do 11 | withdraw_params = build_params(from_id, value) 12 | deposit_params = build_params(to_id, value) 13 | 14 | Multi.new() 15 | |> Multi.merge(fn _changes -> Operation.call(withdraw_params, :withdraw) end) 16 | |> Multi.merge(fn _changes -> Operation.call(deposit_params, :deposit) end) 17 | |> run_transaction() 18 | end 19 | 20 | defp build_params(id, value), do: %{"id" => id, "value" => value} 21 | 22 | defp run_transaction(multi) do 23 | case Repo.transaction(multi) do 24 | {:error, _operation, reason, _changes} -> 25 | {:error, reason} 26 | 27 | {:ok, %{deposit: to_account, withdraw: from_account}} -> 28 | {:ok, TransactionResponse.build(from_account, to_account)} 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/users/create.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Users.Create do 2 | alias Ecto.Multi 3 | alias Rocketpay.{Account, Repo, User} 4 | 5 | def call(params) do 6 | Multi.new() 7 | |> Multi.insert(:create_user, User.changeset(params)) 8 | |> Multi.run( 9 | :create_account, 10 | fn repo, %{create_user: user} -> insert_account(repo, user) end 11 | ) 12 | |> Multi.run( 13 | :preload_data, 14 | fn repo, %{create_user: user} -> preload_data(repo, user) end 15 | ) 16 | |> run_transaction() 17 | end 18 | 19 | defp insert_account(repo, user) do 20 | user.id 21 | |> account_changeset() 22 | |> repo.insert() 23 | end 24 | 25 | defp account_changeset(user_id), do: Account.changeset(%{user_id: user_id, balance: "0.00"}) 26 | 27 | defp run_transaction(multi) do 28 | case Repo.transaction(multi) do 29 | {:error, _operation, reason, _changes} -> {:error, reason} 30 | {:ok, %{preload_data: user}} -> {:ok, user} 31 | end 32 | end 33 | 34 | defp preload_data(repo, user) do 35 | {:ok, repo.preload(user, :account)} 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /moveit/src/styles/components/LevelUpModal.module.css: -------------------------------------------------------------------------------- 1 | .overlay { 2 | background: rgba(242, 243, 245, 0.8); 3 | position: fixed; 4 | top: 0; 5 | bottom: 0; 6 | left: 0; 7 | right: 0; 8 | 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | } 13 | 14 | .container { 15 | background-color: var(--white); 16 | width: 100%; 17 | max-width: 400px; 18 | padding: 2rem 3rem; 19 | border-radius: 5px; 20 | box-shadow: 0 0 60px rgba(0, 0, 0, 0.05); 21 | text-align: center; 22 | position: relative; 23 | } 24 | 25 | .container header { 26 | font-size: 8.75rem; 27 | font-weight: 600; 28 | color: var(--blue); 29 | background: url("/icons/levelup.svg") no-repeat center; 30 | background-size: contain; 31 | } 32 | 33 | .container strong { 34 | font-size: 2.25rem; 35 | color: var(--title); 36 | } 37 | 38 | .container p { 39 | font-size: 1.25rem; 40 | color: var(--text); 41 | margin-top: 0.245rem; 42 | } 43 | 44 | .container button { 45 | position: absolute; 46 | right: 0.5rem; 47 | top: 0.5rem; 48 | background: transparent; 49 | border: 0; 50 | font-size: 0px; 51 | } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jorge Buzeti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /npsapi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "express": "^4.17.1", 8 | "express-async-errors": "^3.1.1", 9 | "handlebars": "^4.7.7", 10 | "nodemailer": "^6.4.18", 11 | "reflect-metadata": "^0.1.13", 12 | "sqlite3": "^5.0.2", 13 | "typeorm": "^0.2.31", 14 | "uuid": "^8.3.2", 15 | "yup": "^0.32.9" 16 | }, 17 | "scripts": { 18 | "dev": "ts-node-dev --transpile-only --ignore-watch node_modules src/server.ts", 19 | "typeorm": "ts-node-dev node_modules/typeorm/cli.js", 20 | "test": "set NODE_ENV=test&&jest -i" 21 | }, 22 | "devDependencies": { 23 | "@types/express": "^4.17.11", 24 | "@types/jest": "^26.0.20", 25 | "@types/mocha": "^8.2.1", 26 | "@types/nodemailer": "^6.4.0", 27 | "@types/supertest": "^2.0.10", 28 | "@types/uuid": "^8.3.0", 29 | "jest": "^26.6.3", 30 | "supertest": "^6.1.3", 31 | "ts-jest": "^26.5.2", 32 | "ts-node-dev": "^1.1.1", 33 | "typescript": "^4.1.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/application.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Application do 2 | # See https://hexdocs.pm/elixir/Application.html 3 | # for more information on OTP Applications 4 | @moduledoc false 5 | 6 | use Application 7 | 8 | def start(_type, _args) do 9 | children = [ 10 | # Start the Ecto repository 11 | Rocketpay.Repo, 12 | # Start the Telemetry supervisor 13 | RocketpayWeb.Telemetry, 14 | # Start the PubSub system 15 | {Phoenix.PubSub, name: Rocketpay.PubSub}, 16 | # Start the Endpoint (http/https) 17 | RocketpayWeb.Endpoint 18 | # Start a worker by calling: Rocketpay.Worker.start_link(arg) 19 | # {Rocketpay.Worker, arg} 20 | ] 21 | 22 | # See https://hexdocs.pm/elixir/Supervisor.html 23 | # for other strategies and supported options 24 | opts = [strategy: :one_for_one, name: Rocketpay.Supervisor] 25 | Supervisor.start_link(children, opts) 26 | end 27 | 28 | # Tell Phoenix to update the endpoint configuration 29 | # whenever the application is updated. 30 | def config_change(changed, _new, removed) do 31 | RocketpayWeb.Endpoint.config_change(changed, removed) 32 | :ok 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/views/error_view.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.ErrorView do 2 | use RocketpayWeb, :view 3 | 4 | import Ecto.Changeset, only: [traverse_errors: 2] 5 | alias Ecto.Changeset 6 | 7 | # If you want to customize a particular status code 8 | # for a certain format, you may uncomment below. 9 | # def render("500.json", _assigns) do 10 | # %{errors: %{detail: "Internal Server Error"}} 11 | # end 12 | 13 | # By default, Phoenix returns the status message from 14 | # the template name. For example, "404.json" becomes 15 | # "Not Found". 16 | def template_not_found(template, _assigns) do 17 | %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}} 18 | end 19 | 20 | def render("400.json", %{result: %Changeset{} = changeset}) do 21 | %{message: translate_errors(changeset)} 22 | end 23 | 24 | def render("400.json", %{result: message}) do 25 | %{message: message} 26 | end 27 | 28 | defp translate_errors(changeset) do 29 | traverse_errors(changeset, fn {msg, opts} -> 30 | Enum.reduce(opts, msg, fn {key, value}, acc -> 31 | String.replace(acc, "%{#{key}}", to_string(value)) 32 | end) 33 | end) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /rocketpay/test/rocketpay/users/create_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Users.CreateTest do 2 | use Rocketpay.DataCase, async: true 3 | 4 | alias Rocketpay.User 5 | alias Rocketpay.Users.Create 6 | 7 | describe "call/1" do 8 | test "when all params are valid, returns an user" do 9 | params = %{ 10 | name: "FakeName", 11 | password: "FakePassword", 12 | nickname: "FakeNickname", 13 | email: "Fake@email.com", 14 | age: 31 15 | } 16 | 17 | {:ok, %User{id: user_id}} = Create.call(params) 18 | 19 | user = Repo.get(User, user_id) 20 | 21 | assert %User{name: "FakeName", age: 31, id: ^user_id} = user 22 | end 23 | 24 | test "when there are invalid params, returns an error" do 25 | params = %{ 26 | name: "FakeName", 27 | nickname: "FakeNickname", 28 | email: "Fake@email.com", 29 | age: 15 30 | } 31 | 32 | {:error, changeset} = Create.call(params) 33 | 34 | expected_response = %{ 35 | age: ["must be greater than or equal to 18"], 36 | password: ["can't be blank"] 37 | } 38 | 39 | assert errors_on(changeset) == expected_response 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /npsapi/src/controllers/UserController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getCustomRepository } from "typeorm"; 3 | import { UsersRepository } from "../repositories/UserRepository"; 4 | import * as yup from "yup"; 5 | import { AppError } from "../errors/AppError"; 6 | 7 | class UserController { 8 | async create(req: Request, res: Response) { 9 | const { name, email } = req.body; 10 | 11 | const schema = yup.object().shape({ 12 | name: yup.string().required(), 13 | email: yup.string().email().required(), 14 | }); 15 | 16 | try { 17 | await schema.validate(req.body, { abortEarly: false }); 18 | } catch (error) { 19 | throw new AppError(error); 20 | } 21 | 22 | const usersRepository = getCustomRepository(UsersRepository); 23 | const userAlreadyExists = await usersRepository.findOne({ 24 | email, 25 | }); 26 | const user = usersRepository.create({ 27 | name, 28 | email, 29 | }); 30 | 31 | if (userAlreadyExists) { 32 | throw new AppError("User already exists!"); 33 | } 34 | 35 | await usersRepository.save(user); 36 | return res.status(201).json(user); 37 | } 38 | } 39 | 40 | export { UserController }; 41 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/views/accounts_view.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.AccountsView do 2 | use RocketpayWeb, :view 3 | 4 | alias Rocketpay.Account 5 | 6 | alias Rocketpay.Accounts.Transactions.Response, as: TransactionResponse 7 | 8 | def render( 9 | "update.json", 10 | %{ 11 | account: %Account{ 12 | id: account_id, 13 | balance: balance 14 | } 15 | } 16 | ) do 17 | %{ 18 | message: "Ballance changed Successfully", 19 | account: %{ 20 | id: account_id, 21 | balance: balance 22 | } 23 | } 24 | end 25 | 26 | def render( 27 | "transaction.json", 28 | %{ 29 | transaction: %TransactionResponse{ 30 | to_account: to_account, 31 | from_account: from_account 32 | } 33 | } 34 | ) do 35 | %{ 36 | message: "Transaction done Successfully", 37 | transaction: %{ 38 | from_account: %{ 39 | id: from_account.id, 40 | balance: from_account.balance 41 | }, 42 | to_account: %{ 43 | id: to_account.id, 44 | balance: to_account.balance 45 | } 46 | } 47 | } 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/channels/user_socket.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.UserSocket do 2 | use Phoenix.Socket 3 | 4 | ## Channels 5 | # channel "room:*", RocketpayWeb.RoomChannel 6 | 7 | # Socket params are passed from the client and can 8 | # be used to verify and authenticate a user. After 9 | # verification, you can put default assigns into 10 | # the socket that will be set for all channels, ie 11 | # 12 | # {:ok, assign(socket, :user_id, verified_user_id)} 13 | # 14 | # To deny connection, return `:error`. 15 | # 16 | # See `Phoenix.Token` documentation for examples in 17 | # performing token verification on connect. 18 | @impl true 19 | def connect(_params, socket, _connect_info) do 20 | {:ok, socket} 21 | end 22 | 23 | # Socket id's are topics that allow you to identify all sockets for a given user: 24 | # 25 | # def id(socket), do: "user_socket:#{socket.assigns.user_id}" 26 | # 27 | # Would allow you to broadcast a "disconnect" event and terminate 28 | # all active sockets and channels for a given user: 29 | # 30 | # RocketpayWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{}) 31 | # 32 | # Returning `nil` makes this socket anonymous. 33 | @impl true 34 | def id(_socket), do: nil 35 | end 36 | -------------------------------------------------------------------------------- /npsapi/src/controllers/SurveyController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getCustomRepository } from "typeorm"; 3 | import { SurveysRepository } from "../repositories/SurveyRepository"; 4 | import * as Yup from "yup"; 5 | import { AppError } from "../errors/AppError"; 6 | 7 | class SurveyController { 8 | async create(req: Request, res: Response) { 9 | const { title, description } = req.body; 10 | 11 | const schema = Yup.object().shape({ 12 | title: Yup.string().required(), 13 | description: Yup.string().required(), 14 | }); 15 | 16 | try { 17 | await schema.validate(req.body, { abortEarly: false }); 18 | } catch (error) { 19 | throw new AppError(error); 20 | } 21 | 22 | const surveysRepository = getCustomRepository(SurveysRepository); 23 | const survey = surveysRepository.create({ 24 | title, 25 | description, 26 | }); 27 | 28 | await surveysRepository.save(survey); 29 | 30 | return res.status(201).json(survey); 31 | } 32 | 33 | async show(req: Request, res: Response) { 34 | const surveysRepository = getCustomRepository(SurveysRepository); 35 | const all = await surveysRepository.find(); 36 | 37 | return res.json(all); 38 | } 39 | } 40 | 41 | export { SurveyController }; 42 | -------------------------------------------------------------------------------- /rocketpay/test/support/channel_case.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.ChannelCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | channel tests. 5 | 6 | Such tests rely on `Phoenix.ChannelTest` and also 7 | import other functionality to make it easier 8 | to build common data structures and query the data layer. 9 | 10 | Finally, if the test case interacts with the database, 11 | we enable the SQL sandbox, so changes done to the database 12 | are reverted at the end of every test. If you are using 13 | PostgreSQL, you can even run database tests asynchronously 14 | by setting `use RocketpayWeb.ChannelCase, async: true`, although 15 | this option is not recommended for other databases. 16 | """ 17 | 18 | use ExUnit.CaseTemplate 19 | 20 | using do 21 | quote do 22 | # Import conveniences for testing with channels 23 | import Phoenix.ChannelTest 24 | import RocketpayWeb.ChannelCase 25 | 26 | # The default endpoint for testing 27 | @endpoint RocketpayWeb.Endpoint 28 | end 29 | end 30 | 31 | setup tags do 32 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(Rocketpay.Repo) 33 | 34 | unless tags[:async] do 35 | Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, {:shared, self()}) 36 | end 37 | 38 | :ok 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /npsapi/src/services/SendMailService.ts: -------------------------------------------------------------------------------- 1 | import nodemailer, { Transporter } from "nodemailer"; 2 | import fs from "fs"; 3 | import handlebars from "handlebars"; 4 | 5 | class SendMailService { 6 | private client: Transporter; 7 | 8 | constructor() { 9 | nodemailer.createTestAccount().then((account) => { 10 | const transporter = nodemailer.createTransport({ 11 | host: account.smtp.host, 12 | port: account.smtp.port, 13 | secure: account.smtp.secure, 14 | auth: { 15 | user: account.user, 16 | pass: account.pass, 17 | }, 18 | }); 19 | 20 | this.client = transporter; 21 | }); 22 | } 23 | 24 | async execute(to: string, subject: string, variables: object, path: string) { 25 | const templateFileContent = fs.readFileSync(path).toString("utf8"); 26 | const mailTemplateParse = handlebars.compile(templateFileContent); 27 | 28 | const html = mailTemplateParse(variables); 29 | 30 | const message = await this.client.sendMail({ 31 | to, 32 | subject, 33 | html, 34 | from: "NPS ", 35 | }); 36 | 37 | console.log(`Mensagem enviada: ${message.messageId}`); 38 | console.log(`URL de preview: ${nodemailer.getTestMessageUrl(message)}`); 39 | } 40 | } 41 | 42 | export default new SendMailService(); 43 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/views/error_helpers.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.ErrorHelpers do 2 | @moduledoc """ 3 | Conveniences for translating and building error messages. 4 | """ 5 | 6 | @doc """ 7 | Translates an error message using gettext. 8 | """ 9 | def translate_error({msg, opts}) do 10 | # When using gettext, we typically pass the strings we want 11 | # to translate as a static argument: 12 | # 13 | # # Translate "is invalid" in the "errors" domain 14 | # dgettext("errors", "is invalid") 15 | # 16 | # # Translate the number of files with plural rules 17 | # dngettext("errors", "1 file", "%{count} files", count) 18 | # 19 | # Because the error messages we show in our forms and APIs 20 | # are defined inside Ecto, we need to translate them dynamically. 21 | # This requires us to call the Gettext module passing our gettext 22 | # backend as first argument. 23 | # 24 | # Note we use the "errors" domain, which means translations 25 | # should be written to the errors.po file. The :count option is 26 | # set by Ecto and indicates we should also apply plural rules. 27 | if count = opts[:count] do 28 | Gettext.dngettext(RocketpayWeb.Gettext, "errors", msg, msg, count, opts) 29 | else 30 | Gettext.dgettext(RocketpayWeb.Gettext, "errors", msg, opts) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /rocketpay/config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | # 4 | # This configuration file is loaded before any dependency and 5 | # is restricted to this project. 6 | 7 | # General application configuration 8 | use Mix.Config 9 | 10 | config :rocketpay, 11 | ecto_repos: [Rocketpay.Repo] 12 | 13 | # Configures the endpoint 14 | config :rocketpay, RocketpayWeb.Endpoint, 15 | url: [host: "localhost"], 16 | secret_key_base: "301cvOG8/4xHG29TQ899cfWEbKPpdHBY3+up0wMmAPbP5+RT9XHcgqX5AGObqLpe", 17 | render_errors: [view: RocketpayWeb.ErrorView, accepts: ~w(json), layout: false], 18 | pubsub_server: Rocketpay.PubSub, 19 | live_view: [signing_salt: "IT2ylQgY"] 20 | 21 | config :rocketpay, Rocketpay.Repo, 22 | migration_primary_key: [type: :binary_id], 23 | migration_foreign_key: [type: :binary_id] 24 | 25 | config :rocketpay, :basic_auth, 26 | username: "banana", 27 | password: "nanica123" 28 | 29 | 30 | # Configures Elixir's Logger 31 | config :logger, :console, 32 | format: "$time $metadata[$level] $message\n", 33 | metadata: [:request_id] 34 | 35 | # Use Jason for JSON parsing in Phoenix 36 | config :phoenix, :json_library, Jason 37 | 38 | # Import environment specific config. This must remain at the bottom 39 | # of this file so it overrides the configuration defined above. 40 | import_config "#{Mix.env()}.exs" 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 4 | 5 | # Created by https://www.toptal.com/developers/gitignore/api/elixir,phoenix 6 | # Edit at https://www.toptal.com/developers/gitignore?templates=elixir,phoenix 7 | 8 | ### Elixir ### 9 | _build 10 | cover 11 | deps 12 | doc 13 | .fetch 14 | erl_crash.dump 15 | *.ez 16 | *.beam 17 | config/*.secret.exs 18 | .elixir_ls/ 19 | 20 | ### Elixir Patch ### 21 | 22 | ### Phoenix ### 23 | # gitignore template for Phoenix projects 24 | # website: http://www.phoenixframework.org/ 25 | # 26 | # Recommended template: Elixir.gitignore 27 | 28 | # Temporary files 29 | tmp 30 | 31 | # Since we are building assets from web/static, 32 | # we ignore priv/static. You may want to comment 33 | # this depending on your deployment strategy. 34 | /priv/static/ 35 | 36 | # Installer-related files 37 | installer/_build 38 | installer/tmp 39 | installer/doc 40 | installer/deps 41 | 42 | # React app 43 | 44 | # dependencies 45 | node_modules 46 | .pnp 47 | .pnp.js 48 | 49 | # testing 50 | coverage 51 | 52 | # production 53 | build 54 | 55 | # misc 56 | .DS_Store 57 | .env.local 58 | .env.development.local 59 | .env.test.local 60 | .env.production.local 61 | 62 | npm-debug.log* 63 | yarn-debug.log* 64 | yarn-error.log* 65 | 66 | .next 67 | 68 | *.sqlite 69 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/user.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.User do 2 | use Ecto.Schema 3 | import Ecto.Changeset 4 | 5 | alias Ecto.Changeset 6 | alias Rocketpay.Account 7 | 8 | @primary_key {:id, :binary_id, autogenerate: true} 9 | 10 | @required_params [:name, :age, :email, :password, :nickname] 11 | 12 | schema "users" do 13 | field :name, :string 14 | field :age, :integer 15 | field :email, :string 16 | field :password, :string, virtual: true 17 | field :password_hash, :string 18 | field :nickname, :string 19 | 20 | has_one :account, Account 21 | 22 | timestamps() 23 | end 24 | 25 | def changeset(params) do 26 | %__MODULE__{} 27 | |> cast(params, @required_params) 28 | |> validate_required(@required_params) 29 | |> validate_length(:password, min: 6) 30 | |> validate_number(:age, greater_than_or_equal_to: 18) 31 | |> validate_format(:email, ~r/^[a-z0-9.]+@[a-z0-9]+\.[a-z]+\.([a-z]+)?$/i) # http://tinyurl.com/jvplhfk 32 | |> unique_constraint([:email]) 33 | |> unique_constraint([:nickname]) 34 | |> put_password_hash() 35 | end 36 | 37 | defp put_password_hash( 38 | %Changeset{ 39 | valid?: true, 40 | changes: %{ 41 | password: password 42 | } 43 | } = changeset 44 | ) do 45 | change(changeset, Pbkdf2.add_hash(password)) 46 | end 47 | 48 | defp put_password_hash(changeset), do: changeset 49 | end 50 | -------------------------------------------------------------------------------- /rocketpay/test/support/conn_case.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.ConnCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | tests that require setting up a connection. 5 | 6 | Such tests rely on `Phoenix.ConnTest` and also 7 | import other functionality to make it easier 8 | to build common data structures and query the data layer. 9 | 10 | Finally, if the test case interacts with the database, 11 | we enable the SQL sandbox, so changes done to the database 12 | are reverted at the end of every test. If you are using 13 | PostgreSQL, you can even run database tests asynchronously 14 | by setting `use RocketpayWeb.ConnCase, async: true`, although 15 | this option is not recommended for other databases. 16 | """ 17 | 18 | use ExUnit.CaseTemplate 19 | 20 | using do 21 | quote do 22 | # Import conveniences for testing with connections 23 | import Plug.Conn 24 | import Phoenix.ConnTest 25 | import RocketpayWeb.ConnCase 26 | 27 | alias RocketpayWeb.Router.Helpers, as: Routes 28 | 29 | # The default endpoint for testing 30 | @endpoint RocketpayWeb.Endpoint 31 | end 32 | end 33 | 34 | setup tags do 35 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(Rocketpay.Repo) 36 | 37 | unless tags[:async] do 38 | Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, {:shared, self()}) 39 | end 40 | 41 | {:ok, conn: Phoenix.ConnTest.build_conn()} 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /npsapi/src/__tests__/Survey.test.ts: -------------------------------------------------------------------------------- 1 | import request from "supertest"; 2 | import { getConnection } from "typeorm"; 3 | import { app } from "../app"; 4 | 5 | import createConnection from "../database"; 6 | 7 | describe("Surveys", () => { 8 | beforeAll(async () => { 9 | const connection = await createConnection(); 10 | await connection.runMigrations(); 11 | }); 12 | 13 | afterAll(async () => { 14 | const connection = getConnection(); 15 | await connection.dropDatabase(); 16 | await connection.close(); 17 | }); 18 | 19 | it("Should be able to create a new survey", async () => { 20 | const response = await request(app).post("/surveys").send({ 21 | title: "Title Example", 22 | description: "Survey Example", 23 | }); 24 | 25 | expect(response.status).toBe(201); 26 | expect(response.body).toHaveProperty("id"); 27 | }); 28 | 29 | it("Should be not able to create a new survey without required fields", async () => { 30 | const response = await request(app).post("/surveys").send({ 31 | title: "Title Example", 32 | }); 33 | 34 | expect(response.status).toBe(400); 35 | }); 36 | 37 | it("Should be able to get all surveys", async () => { 38 | await request(app).post("/surveys").send({ 39 | title: "Title Example 2", 40 | description: "Survey Example 2", 41 | }); 42 | 43 | const response = await request(app).get("/surveys"); 44 | 45 | expect(response.body.length).toBe(2); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/router.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.Router do 2 | use RocketpayWeb, :router 3 | 4 | import Plug.BasicAuth 5 | 6 | pipeline :api do 7 | plug :accepts, ["json"] 8 | end 9 | 10 | pipeline :auth do 11 | plug :basic_auth, Application.compile_env(:rocketpay, :basic_auth) 12 | end 13 | 14 | scope "/api", RocketpayWeb do 15 | pipe_through :api 16 | 17 | get "/:filename", WelcomeController, :index 18 | 19 | post "/users", UsersController, :create 20 | end 21 | 22 | scope "/api", RocketpayWeb do 23 | pipe_through [:api, :auth] 24 | 25 | post "/accounts/:id/deposit", AccountsController, :deposit 26 | post "/accounts/:id/withdraw", AccountsController, :withdraw 27 | post "/accounts/transaction", AccountsController, :transaction 28 | end 29 | 30 | # Enables LiveDashboard only for development 31 | # 32 | # If you want to use the LiveDashboard in production, you should put 33 | # it behind authentication and allow only admins to access it. 34 | # If your application does not have an admins-only section yet, 35 | # you can use Plug.BasicAuth to set up some basic authentication 36 | # as long as you are also using SSL (which you should anyway). 37 | if Mix.env() in [:dev, :test] do 38 | import Phoenix.LiveDashboard.Router 39 | 40 | scope "/" do 41 | pipe_through [:fetch_session, :protect_from_forgery] 42 | live_dashboard "/dashboard", metrics: RocketpayWeb.Telemetry 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /moveit/public/icons/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /npsapi/src/controllers/NpsController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getCustomRepository, Not, IsNull } from "typeorm"; 3 | import { SurveysUsersRepository } from "../repositories/SurveysUserRepository"; 4 | 5 | class NpsController { 6 | async execute(req: Request, res: Response) { 7 | const { survey_id } = req.params; 8 | const surveysUsersRepository = getCustomRepository(SurveysUsersRepository); 9 | 10 | const surveysUsers = await surveysUsersRepository.find({ 11 | survey_id, 12 | value: Not(IsNull()), 13 | }); 14 | 15 | const detractor = surveysUsers.filter( 16 | (survey) => survey.value >= 0 && survey.value <= 6 17 | ).length; 18 | 19 | const promoters = surveysUsers.filter( 20 | (survey) => survey.value >= 9 && survey.value <= 10 21 | ).length; 22 | 23 | const passive = surveysUsers.filter( 24 | (survey) => survey.value >= 7 && survey.value <= 8 25 | ).length; 26 | 27 | const totalAnwers = surveysUsers.length; 28 | 29 | const calculate = Math.floor(((promoters - detractor) / totalAnwers) * 100); 30 | let NpsStatus = ""; 31 | 32 | if (calculate >= 0 && calculate < 50) { 33 | NpsStatus = "Bad"; 34 | } else if (calculate >= 50 || calculate < 80) { 35 | NpsStatus = "Good"; 36 | } else { 37 | NpsStatus = "Excellent"; 38 | } 39 | 40 | return res.json({ 41 | detractor, 42 | promoters, 43 | passive, 44 | totalAnwers, 45 | nps: calculate, 46 | status: NpsStatus, 47 | }); 48 | } 49 | } 50 | 51 | export { NpsController }; 52 | -------------------------------------------------------------------------------- /rocketpay/README.md: -------------------------------------------------------------------------------- 1 |

2 | Rocket Pay | NLW#4 3 |

4 | 5 |

Application developed in the fourth edition of Rocketseat Next Level Week

6 | 7 |

8 | The project • 9 | Technologies • 10 | Contribution • 11 | Author • 12 | License 13 |

14 | 15 | ## 🎯 The project 16 | 17 | On the Elixir (Backend) trail, a payment API was developed, with a system to register users and accounts, make deposits, transfers and withdrawals 18 | 19 | ## 🛠 Technologies 20 | 21 | The following tools were used in the construction of the project: 22 | 23 | - [ReactJS](https://reactjs.org) 24 | - [NextJS](https://nextjs.org) 25 | - [NodeJS](https://nodejs.org/en/) 26 | - [TypeScript](https://typescriptlang.org/) 27 | 28 | ## Contributing 29 | 30 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 31 | 32 | 1. Fork the Project 33 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 34 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 35 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 36 | 5. Open a Pull Request 37 | 38 | ## 💻 Author 39 | 40 | Author avatar 41 | 42 | Write by Jorge Buzeti: 43 | [get in touch](https://github.com/R3tr074#-get-in-touch) 44 | 45 | ## License 46 | 47 | Distributed under the MIT License. See [`LICENSE`](/LICENSE) for more information. 48 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/endpoint.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.Endpoint do 2 | use Phoenix.Endpoint, otp_app: :rocketpay 3 | 4 | # The session will be stored in the cookie and signed, 5 | # this means its contents can be read but not tampered with. 6 | # Set :encryption_salt if you would also like to encrypt it. 7 | @session_options [ 8 | store: :cookie, 9 | key: "_rocketpay_key", 10 | signing_salt: "S87DDJbB" 11 | ] 12 | 13 | socket "/socket", RocketpayWeb.UserSocket, 14 | websocket: true, 15 | longpoll: false 16 | 17 | socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] 18 | 19 | # Serve at "/" the static files from "priv/static" directory. 20 | # 21 | # You should set gzip to true if you are running phx.digest 22 | # when deploying your static files in production. 23 | plug Plug.Static, 24 | at: "/", 25 | from: :rocketpay, 26 | gzip: false, 27 | only: ~w(css fonts images js favicon.ico robots.txt) 28 | 29 | # Code reloading can be explicitly enabled under the 30 | # :code_reloader configuration of your endpoint. 31 | if code_reloading? do 32 | plug Phoenix.CodeReloader 33 | plug Phoenix.Ecto.CheckRepoStatus, otp_app: :rocketpay 34 | end 35 | 36 | plug Phoenix.LiveDashboard.RequestLogger, 37 | param_key: "request_logger", 38 | cookie_key: "request_logger" 39 | 40 | plug Plug.RequestId 41 | plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] 42 | 43 | plug Plug.Parsers, 44 | parsers: [:urlencoded, :multipart, :json], 45 | pass: ["*/*"], 46 | json_decoder: Phoenix.json_library() 47 | 48 | plug Plug.MethodOverride 49 | plug Plug.Head 50 | plug Plug.Session, @session_options 51 | plug RocketpayWeb.Router 52 | end 53 | -------------------------------------------------------------------------------- /npsapi/src/database/migrations/1614251053639-CreateSurveysUsers.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner, Table } from "typeorm"; 2 | 3 | export class CreateSurveysUsers1614251053639 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.createTable( 6 | new Table({ 7 | name: "surveys_users", 8 | columns: [ 9 | { 10 | name: "id", 11 | type: "uuid", 12 | isPrimary: true, 13 | }, 14 | { 15 | name: "user_id", 16 | type: "uuid", 17 | }, 18 | { 19 | name: "survey_id", 20 | type: "uuid", 21 | }, 22 | { 23 | name: "value", 24 | type: "number", 25 | isNullable: true, 26 | }, 27 | { 28 | name: "created_at", 29 | type: "timestamp", 30 | default: "now()", 31 | }, 32 | ], 33 | foreignKeys: [ 34 | { 35 | name: "FKUser", 36 | referencedTableName: "users", 37 | referencedColumnNames: ["id"], 38 | columnNames: ["user_id"], 39 | onDelete: "CASCADE", 40 | onUpdate: "CASCADE", 41 | }, 42 | { 43 | name: "FKSurvey", 44 | referencedTableName: "surveys", 45 | referencedColumnNames: ["id"], 46 | columnNames: ["survey_id"], 47 | onDelete: "CASCADE", 48 | onUpdate: "CASCADE", 49 | }, 50 | ], 51 | }) 52 | ); 53 | } 54 | 55 | public async down(queryRunner: QueryRunner): Promise { 56 | await queryRunner.dropTable("surveys_users"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /npsapi/README.md: -------------------------------------------------------------------------------- 1 |

2 | nps API | NLW#4 3 |

4 | 5 |

Application developed in the fourth edition of Rocketseat Next Level Week

6 | 7 |

8 | The project • 9 | Technologies • 10 | Contribution • 11 | Author • 12 | License 13 |

14 | 15 | ## 🎯 The project 16 | 17 | On the NodeJs track (Backend), an NPS API (Net Promoter Score) was developed, with a system for registering users, registering surveys, sending email with surveys and calculating the NPS. 18 | 19 | ## 🛠 Technologies 20 | 21 | The following tools were used in the construction of the project: 22 | 23 | - [Express](https://expressjs.com) 24 | - [TypeORM](https://typeorm.io) 25 | - [Jest](https://jestjs.io) 26 | - [Nodemailer](https://nodemailer.com) 27 | - [TypeScript](https://typescriptlang.org) 28 | 29 | ## Contributing 30 | 31 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 32 | 33 | 1. Fork the Project 34 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 35 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 36 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 37 | 5. Open a Pull Request 38 | 39 | ## 💻 Author 40 | 41 | Author avatar 42 | 43 | Write by Jorge Buzeti: 44 | [get in touch](https://github.com/R3tr074#-get-in-touch) 45 | 46 | ## License 47 | 48 | Distributed under the MIT License. See [`LICENSE`](/LICENSE) for more information. 49 | -------------------------------------------------------------------------------- /rocketpay/test/support/data_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.DataCase do 2 | @moduledoc """ 3 | This module defines the setup for tests requiring 4 | access to the application's data layer. 5 | 6 | You may define functions here to be used as helpers in 7 | your tests. 8 | 9 | Finally, if the test case interacts with the database, 10 | we enable the SQL sandbox, so changes done to the database 11 | are reverted at the end of every test. If you are using 12 | PostgreSQL, you can even run database tests asynchronously 13 | by setting `use Rocketpay.DataCase, async: true`, although 14 | this option is not recommended for other databases. 15 | """ 16 | 17 | use ExUnit.CaseTemplate 18 | 19 | using do 20 | quote do 21 | alias Rocketpay.Repo 22 | 23 | import Ecto 24 | import Ecto.Changeset 25 | import Ecto.Query 26 | import Rocketpay.DataCase 27 | end 28 | end 29 | 30 | setup tags do 31 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(Rocketpay.Repo) 32 | 33 | unless tags[:async] do 34 | Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, {:shared, self()}) 35 | end 36 | 37 | :ok 38 | end 39 | 40 | @doc """ 41 | A helper that transforms changeset errors into a map of messages. 42 | 43 | assert {:error, changeset} = Accounts.create_user(%{password: "short"}) 44 | assert "password is too short" in errors_on(changeset).password 45 | assert %{password: ["password is too short"]} = errors_on(changeset) 46 | 47 | """ 48 | def errors_on(changeset) do 49 | Ecto.Changeset.traverse_errors(changeset, fn {message, opts} -> 50 | Regex.replace(~r"%{(\w+)}", message, fn _, key -> 51 | opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string() 52 | end) 53 | end) 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /moveit/public/icons/level.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /rocketpay/config/dev.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # Configure your database 4 | config :rocketpay, Rocketpay.Repo, 5 | username: "postgres", 6 | password: "postgres", 7 | database: "rocketpay_dev", 8 | hostname: "localhost", 9 | show_sensitive_data_on_connection_error: true, 10 | pool_size: 10 11 | 12 | # For development, we disable any cache and enable 13 | # debugging and code reloading. 14 | # 15 | # The watchers configuration can be used to run external 16 | # watchers to your application. For example, we use it 17 | # with webpack to recompile .js and .css sources. 18 | config :rocketpay, RocketpayWeb.Endpoint, 19 | http: [port: 4000], 20 | debug_errors: true, 21 | code_reloader: true, 22 | check_origin: false, 23 | watchers: [] 24 | 25 | # ## SSL Support 26 | # 27 | # In order to use HTTPS in development, a self-signed 28 | # certificate can be generated by running the following 29 | # Mix task: 30 | # 31 | # mix phx.gen.cert 32 | # 33 | # Note that this task requires Erlang/OTP 20 or later. 34 | # Run `mix help phx.gen.cert` for more information. 35 | # 36 | # The `http:` config above can be replaced with: 37 | # 38 | # https: [ 39 | # port: 4001, 40 | # cipher_suite: :strong, 41 | # keyfile: "priv/cert/selfsigned_key.pem", 42 | # certfile: "priv/cert/selfsigned.pem" 43 | # ], 44 | # 45 | # If desired, both `http:` and `https:` keys can be 46 | # configured to run both http and https servers on 47 | # different ports. 48 | 49 | # Do not include metadata nor timestamps in development logs 50 | config :logger, :console, format: "[$level] $message\n" 51 | 52 | # Set a higher stacktrace during development. Avoid configuring such 53 | # in production as building large stacktraces may be expensive. 54 | config :phoenix, :stacktrace_depth, 20 55 | 56 | # Initialize plugs at runtime for faster development compilation 57 | config :phoenix, :plug_init_mode, :runtime 58 | -------------------------------------------------------------------------------- /moveit/README.md: -------------------------------------------------------------------------------- 1 |

2 | Move it | NLW#4 3 |

4 | 5 |

Application developed in the fourth edition of Rocketseat Next Level Week

6 | 7 |

8 | The project • 9 | Technologies • 10 | Contribution • 11 | Author • 12 | License 13 |

14 | 15 | ## 🎯 The project 16 | 17 | Track your time, be more productive and take care of your health. Move it was developed for time management, as in the pomodoro technique, dividing the work into 25 minute periods. After that time it releases a challenge, which is some stretching for the body or exercise for the eyes. Each challenge has its xp points and, accumulating the points you level up. 18 | 19 | ## 🛠 Technologies 20 | 21 | The following tools were used in the construction of the project: 22 | 23 | - [ReactJS](https://reactjs.org) 24 | - [NextJS](https://nextjs.org) 25 | - [NodeJS](https://nodejs.org/en/) 26 | - [TypeScript](https://typescriptlang.org/) 27 | 28 | ## Contributing 29 | 30 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 31 | 32 | 1. Fork the Project 33 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 34 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 35 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 36 | 5. Open a Pull Request 37 | 38 | ## 💻 Author 39 | 40 | Author avatar 41 | 42 | Write by Jorge Buzeti: 43 | [get in touch](https://github.com/R3tr074#-get-in-touch) 44 | 45 | ## License 46 | 47 | Distributed under the MIT License. See [`LICENSE`](/LICENSE) for more information. 48 | -------------------------------------------------------------------------------- /moveit/src/context/CountdownContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useEffect, useState } from "react"; 2 | import { ChallengeContext } from "./ChallengesContext"; 3 | 4 | interface CountdownContextData { 5 | minutes: number; 6 | seconds: number; 7 | hasFinished: boolean; 8 | isActive: boolean; 9 | totalTime: number; 10 | startCountdown: () => void; 11 | resetCountdown: () => void; 12 | time: number; 13 | } 14 | 15 | export const CountdownContext = createContext({} as CountdownContextData); 16 | 17 | let countdownTimeout: NodeJS.Timeout; 18 | export const CountdownProvider: React.FC = ({ children }) => { 19 | const { startNewChallenge } = useContext(ChallengeContext); 20 | 21 | const totalTime = 25 * 60; // 25 minutes 22 | const [time, setTime] = useState(totalTime); 23 | const [isActive, setIsActive] = useState(false); 24 | const [hasFinished, setHasFinished] = useState(false); 25 | 26 | const minutes = Math.floor(time / 60); 27 | const seconds = time % 60; 28 | 29 | function startCountdown() { 30 | setIsActive(true); 31 | } 32 | 33 | function resetCountdown() { 34 | clearTimeout(countdownTimeout); 35 | setIsActive(false); 36 | setTime(totalTime); 37 | setHasFinished(false); 38 | } 39 | 40 | useEffect(() => { 41 | if (isActive && time > 0) { 42 | countdownTimeout = setTimeout(() => setTime(time - 1), 1000); 43 | } else if (isActive && time === 0) { 44 | setHasFinished(true); 45 | setIsActive(false); 46 | startNewChallenge(); 47 | } 48 | }, [isActive, time]); 49 | 50 | return ( 51 | 63 | {children} 64 | 65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /moveit/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import { GetServerSideProps } from "next"; 3 | 4 | import { Profile } from "@components/Profile"; 5 | import { Countdown } from "src/components/Countdown"; 6 | import { ChallengeBox } from "src/components/ChallengeBox"; 7 | import { ExperienceBar } from "@components/ExperienceBar"; 8 | import { CompletedChallenges } from "src/components/CompletedChallenges"; 9 | import { ChallengeProvider } from "@context/ChallengesContext"; 10 | 11 | import styles from "@styles/pages/Home.module.css"; 12 | import { CountdownProvider } from "src/context/CountdownContext"; 13 | 14 | interface HomeProps { 15 | level: number; 16 | currentExperience: number; 17 | challengesCompleted: number; 18 | } 19 | 20 | export default function Home({ 21 | level, 22 | currentExperience, 23 | challengesCompleted, 24 | }: HomeProps) { 25 | return ( 26 | 31 |
32 | 33 | Inicio | move.it 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 |
52 | ); 53 | } 54 | 55 | export const getServerSideProps: GetServerSideProps = async (context) => { 56 | const { level, currentExperience, challengesCompleted } = context.req.cookies; 57 | 58 | return { 59 | props: { 60 | level: Number(level), 61 | currentExperience: Number(currentExperience), 62 | challengesCompleted: Number(challengesCompleted), 63 | }, 64 | }; 65 | }; 66 | -------------------------------------------------------------------------------- /moveit/src/styles/components/Countdown.module.css: -------------------------------------------------------------------------------- 1 | .countdownContainer { 2 | display: flex; 3 | align-items: center; 4 | font-family: Rajdhani; 5 | font-weight: 600; 6 | color: var(--title); 7 | } 8 | 9 | .countdownContainer > div { 10 | flex: 1; 11 | 12 | display: flex; 13 | align-items: center; 14 | justify-content: space-evenly; 15 | 16 | background: var(--white); 17 | box-shadow: 0 0 60px rgba(0, 0, 0, 0.05); 18 | border-radius: 5px; 19 | font-size: 8.5rem; 20 | text-align: center; 21 | } 22 | 23 | .countdownContainer > div span { 24 | flex: 1; 25 | } 26 | 27 | .countdownContainer > div span:first-child { 28 | border-right: 1px solid #f0f1f3; 29 | } 30 | 31 | .countdownContainer > div span:last-child { 32 | border-left: 1px solid #f0f1f3; 33 | } 34 | 35 | .countdownContainer > span { 36 | font-size: 6.25rem; 37 | margin: 0 0.5rem; 38 | } 39 | 40 | .countdownButton { 41 | width: 100%; 42 | height: 5rem; 43 | 44 | margin-top: 2rem; 45 | 46 | display: flex; 47 | align-items: center; 48 | justify-content: center; 49 | 50 | border: 0; 51 | border-radius: 5px; 52 | 53 | background: var(--blue); 54 | color: var(--white); 55 | 56 | font-size: 1.25rem; 57 | font-weight: 600; 58 | transition: background-color 0.2s; 59 | } 60 | 61 | .countdownButton svg { 62 | margin-left: 0.5rem; 63 | } 64 | 65 | .countdownButton:not(:disabled):hover { 66 | background: var(--blue-dark); 67 | } 68 | 69 | .countdownButtonActive { 70 | background: var(--white); 71 | color: var(--title); 72 | } 73 | 74 | .countdownButtonActive:not(:disabled):hover { 75 | background: var(--red); 76 | color: var(--white); 77 | } 78 | 79 | .countdownButton:disabled { 80 | background: var(--white); 81 | color: var(--text); 82 | cursor: not-allowed; 83 | border-bottom: 4px solid var(--green); 84 | } 85 | 86 | .progressBar { 87 | position: relative; 88 | height: 5px; 89 | background: var(--green); 90 | transform: translateY(-100%); 91 | border-radius: 5px; 92 | transition: width 0.2s; 93 | } 94 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web/telemetry.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.Telemetry do 2 | use Supervisor 3 | import Telemetry.Metrics 4 | 5 | def start_link(arg) do 6 | Supervisor.start_link(__MODULE__, arg, name: __MODULE__) 7 | end 8 | 9 | @impl true 10 | def init(_arg) do 11 | children = [ 12 | # Telemetry poller will execute the given period measurements 13 | # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics 14 | {:telemetry_poller, measurements: periodic_measurements(), period: 10_000} 15 | # Add reporters as children of your supervision tree. 16 | # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()} 17 | ] 18 | 19 | Supervisor.init(children, strategy: :one_for_one) 20 | end 21 | 22 | def metrics do 23 | [ 24 | # Phoenix Metrics 25 | summary("phoenix.endpoint.stop.duration", 26 | unit: {:native, :millisecond} 27 | ), 28 | summary("phoenix.router_dispatch.stop.duration", 29 | tags: [:route], 30 | unit: {:native, :millisecond} 31 | ), 32 | 33 | # Database Metrics 34 | summary("rocketpay.repo.query.total_time", unit: {:native, :millisecond}), 35 | summary("rocketpay.repo.query.decode_time", unit: {:native, :millisecond}), 36 | summary("rocketpay.repo.query.query_time", unit: {:native, :millisecond}), 37 | summary("rocketpay.repo.query.queue_time", unit: {:native, :millisecond}), 38 | summary("rocketpay.repo.query.idle_time", unit: {:native, :millisecond}), 39 | 40 | # VM Metrics 41 | summary("vm.memory.total", unit: {:byte, :kilobyte}), 42 | summary("vm.total_run_queue_lengths.total"), 43 | summary("vm.total_run_queue_lengths.cpu"), 44 | summary("vm.total_run_queue_lengths.io") 45 | ] 46 | end 47 | 48 | defp periodic_measurements do 49 | [ 50 | # A module, function and arguments to be invoked periodically. 51 | # This function must call :telemetry.execute/3 and a metric must be added above. 52 | # {RocketpayWeb, :count_users, []} 53 | ] 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /moveit/src/components/ChallengeBox.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import styles from "@styles/components/ChallengeBox.module.css"; 3 | import { ChallengeContext } from "src/context/ChallengesContext"; 4 | import { CountdownContext } from "src/context/CountdownContext"; 5 | 6 | export const ChallengeBox: React.FC = () => { 7 | const { activeChallenge, resetChallenge, completeChallenge } = useContext( 8 | ChallengeContext 9 | ); 10 | const { resetCountdown } = useContext(CountdownContext); 11 | const hasActiveChallenge = !!activeChallenge; 12 | 13 | function handleChallengeSucceed() { 14 | completeChallenge(); 15 | resetCountdown(); 16 | } 17 | 18 | function handleChallengeFailed() { 19 | resetChallenge(); 20 | resetCountdown(); 21 | } 22 | 23 | return ( 24 |
25 | {hasActiveChallenge ? ( 26 |
27 |
Ganhe {activeChallenge.amount} xp
28 | 29 |
30 | 31 | Novo desafio 32 |

{activeChallenge.description}

33 |
34 | 35 |
36 | 43 | 50 |
51 |
52 | ) : ( 53 |
54 | Finalize um ciclo para receber um desafio 55 |

56 | Level Up 57 | Avançe de level completando desafios 58 |

59 |
60 | )} 61 |
62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay_web.ex: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb do 2 | @moduledoc """ 3 | The entrypoint for defining your web interface, such 4 | as controllers, views, channels and so on. 5 | 6 | This can be used in your application as: 7 | 8 | use RocketpayWeb, :controller 9 | use RocketpayWeb, :view 10 | 11 | The definitions below will be executed for every view, 12 | controller, etc, so keep them short and clean, focused 13 | on imports, uses and aliases. 14 | 15 | Do NOT define functions inside the quoted expressions 16 | below. Instead, define any helper function in modules 17 | and import those modules here. 18 | """ 19 | 20 | def controller do 21 | quote do 22 | use Phoenix.Controller, namespace: RocketpayWeb 23 | 24 | import Plug.Conn 25 | import RocketpayWeb.Gettext 26 | alias RocketpayWeb.Router.Helpers, as: Routes 27 | end 28 | end 29 | 30 | def view do 31 | quote do 32 | use Phoenix.View, 33 | root: "lib/rocketpay_web/templates", 34 | namespace: RocketpayWeb 35 | 36 | # Import convenience functions from controllers 37 | import Phoenix.Controller, 38 | only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1] 39 | 40 | # Include shared imports and aliases for views 41 | unquote(view_helpers()) 42 | end 43 | end 44 | 45 | def router do 46 | quote do 47 | use Phoenix.Router 48 | 49 | import Plug.Conn 50 | import Phoenix.Controller 51 | end 52 | end 53 | 54 | def channel do 55 | quote do 56 | use Phoenix.Channel 57 | import RocketpayWeb.Gettext 58 | end 59 | end 60 | 61 | defp view_helpers do 62 | quote do 63 | # Import basic rendering functionality (render, render_layout, etc) 64 | import Phoenix.View 65 | 66 | import RocketpayWeb.ErrorHelpers 67 | import RocketpayWeb.Gettext 68 | alias RocketpayWeb.Router.Helpers, as: Routes 69 | end 70 | end 71 | 72 | @doc """ 73 | When used, dispatch to the appropriate controller/view/etc. 74 | """ 75 | defmacro __using__(which) when is_atom(which) do 76 | apply(__MODULE__, which, []) 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /rocketpay/config/prod.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # For production, don't forget to configure the url host 4 | # to something meaningful, Phoenix uses this information 5 | # when generating URLs. 6 | # 7 | # Note we also include the path to a cache manifest 8 | # containing the digested version of static files. This 9 | # manifest is generated by the `mix phx.digest` task, 10 | # which you should run after static files are built and 11 | # before starting your production server. 12 | config :rocketpay, RocketpayWeb.Endpoint, 13 | url: [host: "example.com", port: 80], 14 | cache_static_manifest: "priv/static/cache_manifest.json" 15 | 16 | # Do not print debug messages in production 17 | config :logger, level: :info 18 | 19 | # ## SSL Support 20 | # 21 | # To get SSL working, you will need to add the `https` key 22 | # to the previous section and set your `:url` port to 443: 23 | # 24 | # config :rocketpay, RocketpayWeb.Endpoint, 25 | # ... 26 | # url: [host: "example.com", port: 443], 27 | # https: [ 28 | # port: 443, 29 | # cipher_suite: :strong, 30 | # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), 31 | # certfile: System.get_env("SOME_APP_SSL_CERT_PATH"), 32 | # transport_options: [socket_opts: [:inet6]] 33 | # ] 34 | # 35 | # The `cipher_suite` is set to `:strong` to support only the 36 | # latest and more secure SSL ciphers. This means old browsers 37 | # and clients may not be supported. You can set it to 38 | # `:compatible` for wider support. 39 | # 40 | # `:keyfile` and `:certfile` expect an absolute path to the key 41 | # and cert in disk or a relative path inside priv, for example 42 | # "priv/ssl/server.key". For all supported SSL configuration 43 | # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1 44 | # 45 | # We also recommend setting `force_ssl` in your endpoint, ensuring 46 | # no data is ever sent via http, always redirecting to https: 47 | # 48 | # config :rocketpay, RocketpayWeb.Endpoint, 49 | # force_ssl: [hsts: true] 50 | # 51 | # Check `Plug.SSL` for all available options in `force_ssl`. 52 | 53 | # Finally import the config/prod.secret.exs which loads secrets 54 | # and configuration from environment variables. 55 | import_config "prod.secret.exs" 56 | -------------------------------------------------------------------------------- /moveit/src/components/Countdown.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import styles from "@styles/components/Countdown.module.css"; 3 | import { CountdownContext } from "src/context/CountdownContext"; 4 | import { FiPlayCircle, FiXCircle as FiClose } from "react-icons/fi"; 5 | 6 | export const Countdown: React.FC = () => { 7 | const { 8 | minutes, 9 | seconds, 10 | hasFinished, 11 | isActive, 12 | resetCountdown, 13 | startCountdown, 14 | totalTime, 15 | time, 16 | } = useContext(CountdownContext); 17 | 18 | const [minuteLeft, minuteRight] = String(minutes).padStart(2, "0").split(""); 19 | const [secondLeft, secondRight] = String(seconds).padStart(2, "0").split(""); 20 | 21 | const percentProgress = 100 - Math.round(time * 100) / totalTime; 22 | 23 | return ( 24 |
25 |
26 |
27 | {minuteLeft} 28 | {minuteRight} 29 |
30 | : 31 |
32 | {secondLeft} 33 | {secondRight} 34 |
35 |
36 | 37 | {hasFinished ? ( 38 | 41 | ) : ( 42 | <> 43 | {isActive ? ( 44 | <> 45 | 53 |
57 | 58 | ) : ( 59 | 67 | )} 68 | 69 | )} 70 |
71 | ); 72 | }; 73 | -------------------------------------------------------------------------------- /moveit/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 | ] 63 | -------------------------------------------------------------------------------- /rocketpay/lib/rocketpay/accounts/operation.ex: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.Accounts.Operation do 2 | alias Ecto.Multi 3 | 4 | alias Rocketpay.Account 5 | 6 | def call(%{"id" => id, "value" => value}, operation) do 7 | operation_name = account_operation_name(operation) 8 | 9 | Multi.new() 10 | |> Multi.run(operation_name, fn repo, _changes -> get_account(repo, id) end) 11 | |> Multi.run(operation, fn repo, changes -> 12 | account = Map.get(changes, operation_name) 13 | update_balance(repo, account, value, operation) 14 | end) 15 | end 16 | 17 | defp get_account(repo, id) do 18 | case repo.get(Account, id) do 19 | nil -> {:error, "Account not found!"} 20 | account -> {:ok, account} 21 | end 22 | end 23 | 24 | defp update_balance(repo, account, value, operation) do 25 | account 26 | |> operation(value, operation) 27 | |> update_account(repo, account) 28 | end 29 | 30 | defp operation(%Account{balance: balance}, value, operation) do 31 | value 32 | |> Decimal.cast() 33 | |> handle_cast(balance, operation) 34 | end 35 | 36 | defp handle_cast( 37 | { 38 | :ok, 39 | %Decimal{ 40 | sign: 1 41 | } = value 42 | }, 43 | balance, 44 | :deposit 45 | ), 46 | do: Decimal.add(balance, value) 47 | 48 | defp handle_cast( 49 | { 50 | :ok, 51 | %Decimal{ 52 | sign: 1 53 | } = value 54 | }, 55 | balance, 56 | :withdraw 57 | ), 58 | do: Decimal.sub(balance, value) 59 | 60 | defp handle_cast( 61 | { 62 | :ok, 63 | %Decimal{ 64 | sign: -1 65 | } 66 | }, 67 | _balance, 68 | _operation 69 | ), 70 | do: {:error, "Negative values are not supported"} 71 | 72 | defp handle_cast(:error, _balance, _operation), do: {:error, "Invalid value!"} 73 | 74 | defp update_account({:error, _reason} = error, _repo, _account), do: error 75 | 76 | defp update_account(value, repo, account) do 77 | account 78 | |> Account.changeset(%{balance: value}) 79 | |> repo.update() 80 | end 81 | 82 | defp account_operation_name(operation) do 83 | "account_#{Atom.to_string(operation)}" |> String.to_atom() 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /npsapi/src/views/emails/npsMail.hbs: -------------------------------------------------------------------------------- 1 | 57 | 58 |
59 | 60 | 61 |

{{ title }}

62 |
63 |

{{ description }} 64 |

65 | 66 |

67 | Pouco provável 68 | Muito provável 69 |
70 | 71 |
72 | 1 73 | 2 74 | 3 75 | 4 76 | 5 77 | 6 78 | 7 79 | 8 80 | 9 81 | 10 82 |
83 | 84 |
85 |
86 | Sua opinião é muito importante para nós! 87 |

🚀 Equipe NLW!!

88 |
-------------------------------------------------------------------------------- /moveit/src/styles/components/ChallengeBox.module.css: -------------------------------------------------------------------------------- 1 | .challengeBoxContainer { 2 | height: 100%; 3 | 4 | background: var(--white); 5 | border-radius: 5px; 6 | box-shadow: 0 0 60px rgba(0, 0, 0, 0.05); 7 | padding: 1.5rem 2rem; 8 | 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | justify-content: center; 13 | 14 | text-align: center; 15 | } 16 | 17 | .challengeNotActive { 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | } 22 | 23 | .challengeNotActive strong { 24 | font-size: 1.5rem; 25 | font-weight: 500; 26 | line-height: 1.4; 27 | } 28 | 29 | .challengeNotActive p { 30 | display: flex; 31 | flex-direction: row; 32 | align-items: center; 33 | line-height: 1.4; 34 | max-width: 70%; 35 | margin-top: 3rem; 36 | margin-bottom: 1rem; 37 | } 38 | 39 | .challengeNotActive img { 40 | width: 2.3rem; 41 | } 42 | 43 | .challengeNotActive p > span { 44 | margin-left: 0.7rem; 45 | } 46 | 47 | .challengeActive { 48 | height: 100%; 49 | 50 | display: flex; 51 | flex-direction: column; 52 | } 53 | 54 | .challengeActive header { 55 | color: var(--blue); 56 | font-weight: 600; 57 | font-size: 1.25rem; 58 | padding: 0 2rem 1.5rem; 59 | border-bottom: 1px solid var(--gray-line); 60 | } 61 | 62 | .challengeActive main { 63 | flex: 1; 64 | display: flex; 65 | flex-direction: column; 66 | align-items: center; 67 | justify-content: center; 68 | } 69 | 70 | .challengeActive main strong { 71 | font-size: 2rem; 72 | font-weight: 600; 73 | color: var(--title); 74 | margin: 1.5rem 0 1rem; 75 | } 76 | 77 | .challengeActive main p { 78 | line-height: 1.5; 79 | } 80 | 81 | .challengeActive footer { 82 | display: grid; 83 | grid-template-columns: 1fr 1fr; 84 | gap: 1rem; 85 | } 86 | 87 | .challengeActive footer button { 88 | height: 3rem; 89 | 90 | display: flex; 91 | align-items: center; 92 | justify-content: center; 93 | 94 | border: 0; 95 | border-radius: 5px; 96 | 97 | color: var(--white); 98 | 99 | font-size: 1rem; 100 | font-weight: 600; 101 | transition: filter 0.2s; 102 | } 103 | 104 | .challengeActive footer button.challengeFailedButton { 105 | background: var(--red); 106 | } 107 | 108 | .challengeActive footer button.challengeSucceedButton { 109 | background: var(--green); 110 | } 111 | 112 | .challengeActive footer button:hover { 113 | filter: brightness(0.9); 114 | } 115 | -------------------------------------------------------------------------------- /moveit/public/icons/level-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /rocketpay/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Rocketpay.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :rocketpay, 7 | version: "0.1.0", 8 | elixir: "~> 1.7", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | aliases: aliases(), 13 | deps: deps(), 14 | 15 | test_coverage: [tool: ExCoveralls], 16 | preferred_cli_env: [ 17 | coveralls: :test, 18 | "coveralls.detail": :test, 19 | "coveralls.post": :test, 20 | "coveralls.html": :test, 21 | cover: :test, 22 | "generate.cover": :test 23 | ], 24 | ] 25 | end 26 | 27 | # Configuration for the OTP application. 28 | # 29 | # Type `mix help compile.app` for more information. 30 | def application do 31 | [ 32 | mod: {Rocketpay.Application, []}, 33 | extra_applications: [:logger, :runtime_tools] 34 | ] 35 | end 36 | 37 | # Specifies which paths to compile per environment. 38 | defp elixirc_paths(:test), do: ["lib", "test/support"] 39 | defp elixirc_paths(_), do: ["lib"] 40 | 41 | # Specifies your project dependencies. 42 | # 43 | # Type `mix help deps` for examples and options. 44 | defp deps do 45 | [ 46 | {:phoenix, "~> 1.5.6"}, 47 | {:phoenix_ecto, "~> 4.1"}, 48 | {:ecto_sql, "~> 3.4"}, 49 | {:postgrex, ">= 0.0.0"}, 50 | {:phoenix_live_dashboard, "~> 0.3 or ~> 0.2.9"}, 51 | {:telemetry_metrics, "~> 0.4"}, 52 | {:telemetry_poller, "~> 0.4"}, 53 | {:gettext, "~> 0.11"}, 54 | {:jason, "~> 1.0"}, 55 | {:plug_cowboy, "~> 2.0"}, 56 | {:credo, "~> 1.5", only: [:dev, :test], runtime: false}, 57 | {:pbkdf2_elixir, "~> 1.3"}, 58 | {:decimal, "~> 2.0"}, 59 | {:excoveralls, "~> 0.10", only: :test} 60 | ] 61 | end 62 | 63 | # Aliases are shortcuts or tasks specific to the current project. 64 | # For example, to install project dependencies and perform other setup tasks, run: 65 | # 66 | # $ mix setup 67 | # 68 | # See the documentation for `Mix` for more info on aliases. 69 | defp aliases do 70 | [ 71 | setup: ["deps.get", "ecto.setup"], 72 | "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], 73 | "ecto.reset": ["ecto.drop", "ecto.setup"], 74 | test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"], 75 | cover: ["test --cover"], 76 | "generate.cover": ["coveralls.html"] 77 | ] 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /rocketpay/priv/gettext/en/LC_MESSAGES/errors.po: -------------------------------------------------------------------------------- 1 | ## `msgid`s in this file come from POT (.pot) files. 2 | ## 3 | ## Do not add, change, or remove `msgid`s manually here as 4 | ## they're tied to the ones in the corresponding POT file 5 | ## (with the same domain). 6 | ## 7 | ## Use `mix gettext.extract --merge` or `mix gettext.merge` 8 | ## to merge POT files into PO files. 9 | msgid "" 10 | msgstr "" 11 | "Language: en\n" 12 | 13 | ## From Ecto.Changeset.cast/4 14 | msgid "can't be blank" 15 | msgstr "" 16 | 17 | ## From Ecto.Changeset.unique_constraint/3 18 | msgid "has already been taken" 19 | msgstr "" 20 | 21 | ## From Ecto.Changeset.put_change/3 22 | msgid "is invalid" 23 | msgstr "" 24 | 25 | ## From Ecto.Changeset.validate_acceptance/3 26 | msgid "must be accepted" 27 | msgstr "" 28 | 29 | ## From Ecto.Changeset.validate_format/3 30 | msgid "has invalid format" 31 | msgstr "" 32 | 33 | ## From Ecto.Changeset.validate_subset/3 34 | msgid "has an invalid entry" 35 | msgstr "" 36 | 37 | ## From Ecto.Changeset.validate_exclusion/3 38 | msgid "is reserved" 39 | msgstr "" 40 | 41 | ## From Ecto.Changeset.validate_confirmation/3 42 | msgid "does not match confirmation" 43 | msgstr "" 44 | 45 | ## From Ecto.Changeset.no_assoc_constraint/3 46 | msgid "is still associated with this entry" 47 | msgstr "" 48 | 49 | msgid "are still associated with this entry" 50 | msgstr "" 51 | 52 | ## From Ecto.Changeset.validate_length/3 53 | msgid "should be %{count} character(s)" 54 | msgid_plural "should be %{count} character(s)" 55 | msgstr[0] "" 56 | msgstr[1] "" 57 | 58 | msgid "should have %{count} item(s)" 59 | msgid_plural "should have %{count} item(s)" 60 | msgstr[0] "" 61 | msgstr[1] "" 62 | 63 | msgid "should be at least %{count} character(s)" 64 | msgid_plural "should be at least %{count} character(s)" 65 | msgstr[0] "" 66 | msgstr[1] "" 67 | 68 | msgid "should have at least %{count} item(s)" 69 | msgid_plural "should have at least %{count} item(s)" 70 | msgstr[0] "" 71 | msgstr[1] "" 72 | 73 | msgid "should be at most %{count} character(s)" 74 | msgid_plural "should be at most %{count} character(s)" 75 | msgstr[0] "" 76 | msgstr[1] "" 77 | 78 | msgid "should have at most %{count} item(s)" 79 | msgid_plural "should have at most %{count} item(s)" 80 | msgstr[0] "" 81 | msgstr[1] "" 82 | 83 | ## From Ecto.Changeset.validate_number/3 84 | msgid "must be less than %{number}" 85 | msgstr "" 86 | 87 | msgid "must be greater than %{number}" 88 | msgstr "" 89 | 90 | msgid "must be less than or equal to %{number}" 91 | msgstr "" 92 | 93 | msgid "must be greater than or equal to %{number}" 94 | msgstr "" 95 | 96 | msgid "must be equal to %{number}" 97 | msgstr "" 98 | -------------------------------------------------------------------------------- /rocketpay/priv/gettext/errors.pot: -------------------------------------------------------------------------------- 1 | ## This is a PO Template file. 2 | ## 3 | ## `msgid`s here are often extracted from source code. 4 | ## Add new translations manually only if they're dynamic 5 | ## translations that can't be statically extracted. 6 | ## 7 | ## Run `mix gettext.extract` to bring this file up to 8 | ## date. Leave `msgstr`s empty as changing them here has no 9 | ## effect: edit them in PO (`.po`) files instead. 10 | 11 | ## From Ecto.Changeset.cast/4 12 | msgid "can't be blank" 13 | msgstr "" 14 | 15 | ## From Ecto.Changeset.unique_constraint/3 16 | msgid "has already been taken" 17 | msgstr "" 18 | 19 | ## From Ecto.Changeset.put_change/3 20 | msgid "is invalid" 21 | msgstr "" 22 | 23 | ## From Ecto.Changeset.validate_acceptance/3 24 | msgid "must be accepted" 25 | msgstr "" 26 | 27 | ## From Ecto.Changeset.validate_format/3 28 | msgid "has invalid format" 29 | msgstr "" 30 | 31 | ## From Ecto.Changeset.validate_subset/3 32 | msgid "has an invalid entry" 33 | msgstr "" 34 | 35 | ## From Ecto.Changeset.validate_exclusion/3 36 | msgid "is reserved" 37 | msgstr "" 38 | 39 | ## From Ecto.Changeset.validate_confirmation/3 40 | msgid "does not match confirmation" 41 | msgstr "" 42 | 43 | ## From Ecto.Changeset.no_assoc_constraint/3 44 | msgid "is still associated with this entry" 45 | msgstr "" 46 | 47 | msgid "are still associated with this entry" 48 | msgstr "" 49 | 50 | ## From Ecto.Changeset.validate_length/3 51 | msgid "should be %{count} character(s)" 52 | msgid_plural "should be %{count} character(s)" 53 | msgstr[0] "" 54 | msgstr[1] "" 55 | 56 | msgid "should have %{count} item(s)" 57 | msgid_plural "should have %{count} item(s)" 58 | msgstr[0] "" 59 | msgstr[1] "" 60 | 61 | msgid "should be at least %{count} character(s)" 62 | msgid_plural "should be at least %{count} character(s)" 63 | msgstr[0] "" 64 | msgstr[1] "" 65 | 66 | msgid "should have at least %{count} item(s)" 67 | msgid_plural "should have at least %{count} item(s)" 68 | msgstr[0] "" 69 | msgstr[1] "" 70 | 71 | msgid "should be at most %{count} character(s)" 72 | msgid_plural "should be at most %{count} character(s)" 73 | msgstr[0] "" 74 | msgstr[1] "" 75 | 76 | msgid "should have at most %{count} item(s)" 77 | msgid_plural "should have at most %{count} item(s)" 78 | msgstr[0] "" 79 | msgstr[1] "" 80 | 81 | ## From Ecto.Changeset.validate_number/3 82 | msgid "must be less than %{number}" 83 | msgstr "" 84 | 85 | msgid "must be greater than %{number}" 86 | msgstr "" 87 | 88 | msgid "must be less than or equal to %{number}" 89 | msgstr "" 90 | 91 | msgid "must be greater than or equal to %{number}" 92 | msgstr "" 93 | 94 | msgid "must be equal to %{number}" 95 | msgstr "" 96 | -------------------------------------------------------------------------------- /npsapi/src/controllers/SendMailController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getCustomRepository } from "typeorm"; 3 | import { SurveysRepository } from "../repositories/SurveyRepository"; 4 | import { SurveysUsersRepository } from "../repositories/SurveysUserRepository"; 5 | import { UsersRepository } from "../repositories/UserRepository"; 6 | import { resolve } from "path"; 7 | import SendMailService from "../services/SendMailService"; 8 | import { AppError } from "../errors/AppError"; 9 | import * as Yup from "yup"; 10 | 11 | class SendMailController { 12 | async execute(req: Request, res: Response) { 13 | const { email, survey_id } = req.body; 14 | 15 | const schema = Yup.object().shape({ 16 | email: Yup.string().email().required(), 17 | survey_id: Yup.string().required(), 18 | }); 19 | 20 | try { 21 | await schema.validate(req.body, { abortEarly: false }); 22 | } catch (error) { 23 | throw new AppError(error); 24 | } 25 | 26 | const usersRepository = getCustomRepository(UsersRepository); 27 | const surveyRepository = getCustomRepository(SurveysRepository); 28 | const surveyUsersRepository = getCustomRepository(SurveysUsersRepository); 29 | 30 | const user = await usersRepository.findOne({ email }); 31 | 32 | if (!user) { 33 | throw new AppError("User does not exists"); 34 | } 35 | 36 | const survey = await surveyRepository.findOne({ id: survey_id }); 37 | const npsPath = resolve(__dirname, "..", "views", "emails", "npsMail.hbs"); 38 | 39 | if (!survey) { 40 | throw new AppError("Survey does not exists"); 41 | } 42 | const surveyUserAlreadyExists = await surveyUsersRepository.findOne({ 43 | where: { user_id: user.id, value: null }, 44 | relations: ["user", "survey"], 45 | }); 46 | 47 | const variables = { 48 | name: user.name, 49 | title: survey.title, 50 | description: survey.description, 51 | id: "", 52 | link: process.env.URL_MAIL, 53 | }; 54 | 55 | if (surveyUserAlreadyExists) { 56 | variables.id = surveyUserAlreadyExists.id; 57 | await SendMailService.execute(email, survey.title, variables, npsPath); 58 | return res.json(surveyUserAlreadyExists); 59 | } 60 | 61 | // Salvar às informações na tabela surveyUser 62 | 63 | const surveyUser = surveyUsersRepository.create({ 64 | user_id: user.id, 65 | survey_id: survey_id, 66 | }); 67 | 68 | await surveyUsersRepository.save(surveyUser); 69 | variables.id = surveyUser.id; 70 | 71 | // Enviar e-mail ao usuário 72 | 73 | await SendMailService.execute(email, survey.title, variables, npsPath); 74 | return res.json(surveyUser); 75 | } 76 | } 77 | 78 | export { SendMailController }; 79 | -------------------------------------------------------------------------------- /moveit/public/logo-full.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /rocketpay/test/rocketpay_web/controllers/accounts_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule RocketpayWeb.AccountsControllerTest do 2 | use RocketpayWeb.ConnCase, async: true 3 | 4 | alias Rocketpay.{Account, User} 5 | 6 | describe "deposit/2" do 7 | setup %{conn: conn} do 8 | params = %{ 9 | name: "FakeName", 10 | password: "FakePassword", 11 | nickname: "FakeNickname", 12 | email: "Fake@email.com", 13 | age: 31 14 | } 15 | 16 | {:ok, %User{account: %Account{id: account_id}}} = Rocketpay.create_user(params) 17 | 18 | encoded_auth_basic = Base.encode64("banana:nanica123") 19 | 20 | conn = put_req_header(conn, "authorization", "Basic #{encoded_auth_basic}") 21 | 22 | {:ok, conn: conn, account_id: account_id} 23 | end 24 | 25 | test "when all params are valid, make the deposit", %{conn: conn, account_id: account_id} do 26 | params = %{"value" => "50.00"} 27 | 28 | response = 29 | conn 30 | |> post(Routes.accounts_path(conn, :deposit, account_id, params)) 31 | |> json_response(:ok) 32 | 33 | expected_response = %{ 34 | "account" => %{"balance" => "50.00", "id" => account_id}, 35 | "message" => "Ballance changed Successfully" 36 | } 37 | 38 | assert response == expected_response 39 | end 40 | 41 | test "when there are invalid params, returns an error", %{conn: conn, account_id: account_id} do 42 | params = %{"value" => "banana"} 43 | 44 | response = 45 | conn 46 | |> post(Routes.accounts_path(conn, :deposit, account_id, params)) 47 | |> json_response(:bad_request) 48 | 49 | expected_response = %{"message" => "Invalid value!"} 50 | 51 | assert response == expected_response 52 | end 53 | end 54 | 55 | describe "withdraw/2" do 56 | setup %{conn: conn} do 57 | params = %{ 58 | name: "FakeName", 59 | password: "FakePassword", 60 | nickname: "FakeNickname", 61 | email: "Fake@email.com", 62 | age: 31 63 | } 64 | 65 | {:ok, %User{account: %Account{id: account_id}}} = Rocketpay.create_user(params) 66 | 67 | Rocketpay.deposit_value(%{"id" => account_id, "value" => "500.00"}) 68 | 69 | encoded_auth_basic = Base.encode64("banana:nanica123") 70 | 71 | conn = put_req_header(conn, "authorization", "Basic #{encoded_auth_basic}") 72 | 73 | {:ok, conn: conn, account_id: account_id} 74 | end 75 | 76 | test "when all params are valid, make the withdraw", %{conn: conn, account_id: account_id} do 77 | params = %{"value" => "50.00"} 78 | 79 | response = 80 | conn 81 | |> post(Routes.accounts_path(conn, :withdraw, account_id, params)) 82 | |> json_response(:ok) 83 | 84 | expected_response = %{ 85 | "account" => %{"balance" => "450.00", "id" => account_id}, 86 | "message" => "Ballance changed Successfully" 87 | } 88 | 89 | assert response == expected_response 90 | end 91 | 92 | test "when there are invalid params, returns an error", %{conn: conn, account_id: account_id} do 93 | params = %{"value" => "banana"} 94 | 95 | response = 96 | conn 97 | |> post(Routes.accounts_path(conn, :deposit, account_id, params)) 98 | |> json_response(:bad_request) 99 | 100 | expected_response = %{"message" => "Invalid value!"} 101 | 102 | assert response == expected_response 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /moveit/src/context/ChallengesContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, ReactNode, useEffect, useState } from "react"; 2 | import Cookies from "js-cookie"; 3 | import challenges from "~/../challenges.json"; 4 | import { LevelUpModal } from "@components/LevelUpModal"; 5 | 6 | interface Challenge { 7 | type: "body" | "eye"; 8 | description: string; 9 | amount: number; 10 | } 11 | 12 | interface ChallengesContextData { 13 | level: number; 14 | levelUp: () => void; 15 | currentExperience: number; 16 | challengesCompleted: number; 17 | startNewChallenge: () => void; 18 | activeChallenge: Challenge; 19 | resetChallenge: () => void; 20 | experienceToNextLevel: number; 21 | completeChallenge: () => void; 22 | closeLevelUpModal: () => void; 23 | } 24 | 25 | interface ChallengeProviderProps { 26 | children: ReactNode; 27 | level: number; 28 | currentExperience: number; 29 | challengesCompleted: number; 30 | } 31 | 32 | export const ChallengeContext = createContext({} as ChallengesContextData); 33 | 34 | export const ChallengeProvider = ({ 35 | children, 36 | ...rest 37 | }: ChallengeProviderProps) => { 38 | const [level, setLevel] = useState(rest.level ?? 1); 39 | const [currentExperience, setCurrentExperience] = useState( 40 | rest.currentExperience ?? 0 41 | ); 42 | const [challengesCompleted, setChallengesCompleted] = useState( 43 | rest.challengesCompleted ?? 0 44 | ); 45 | const [activeChallenge, setActiveChallenge] = useState(null); 46 | const [isLevelUpModalOpen, setIsLevelUpModalOpen] = useState(false); 47 | 48 | const experienceToNextLevel = Math.pow((level + 1) * 4, 2); 49 | 50 | useEffect(() => { 51 | Notification.requestPermission(); 52 | }, []); 53 | 54 | useEffect(() => { 55 | Cookies.set("level", String(level)); 56 | Cookies.set("currentExperience", String(currentExperience)); 57 | Cookies.set("challengesCompleted", String(challengesCompleted)); 58 | }, [level, currentExperience, challengesCompleted]); 59 | 60 | function startNewChallenge() { 61 | const randomChallengeIndex = Math.floor(Math.random() * challenges.length); 62 | const challenge = challenges[randomChallengeIndex]; 63 | 64 | setActiveChallenge(challenge); 65 | 66 | new Audio("/notification.mp3").play(); 67 | 68 | if (Notification.permission === "granted") { 69 | new Notification("Novo desafio", { 70 | body: `Valendo ${challenge.amount}xp`, 71 | }); 72 | } 73 | } 74 | 75 | function resetChallenge() { 76 | setActiveChallenge(null); 77 | } 78 | 79 | function completeChallenge() { 80 | if (!activeChallenge) return; // if not has active Challenge, just do nothing 81 | 82 | const { amount } = activeChallenge; 83 | 84 | let finalExperience = currentExperience + amount; 85 | 86 | if (finalExperience >= experienceToNextLevel) { 87 | finalExperience = finalExperience - experienceToNextLevel; 88 | levelUp(); 89 | } 90 | 91 | setCurrentExperience(finalExperience); 92 | setActiveChallenge(null); 93 | setChallengesCompleted(challengesCompleted + 1); 94 | } 95 | 96 | function levelUp() { 97 | setLevel(level + 1); 98 | setIsLevelUpModalOpen(true); 99 | } 100 | 101 | function closeLevelUpModal() { 102 | setIsLevelUpModalOpen(false); 103 | } 104 | 105 | return ( 106 | 120 | {children} 121 | {isLevelUpModalOpen && } 122 | 123 | ); 124 | }; 125 | -------------------------------------------------------------------------------- /moveit/public/icons/levelup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /npsapi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "es2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | // "outDir": "./", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | "removeComments": true /* Do not emit comments to output. */, 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": false /* Enable all strict type-checking options. */, 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | "strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */, 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | 44 | /* Module Resolution Options */ 45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | // "typeRoots": [], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, 64 | "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, 65 | 66 | /* Advanced Options */ 67 | "skipLibCheck": true /* Skip type checking of declaration files. */, 68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /npsapi/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property and type check, visit: 3 | * https://jestjs.io/docs/en/configuration.html 4 | */ 5 | 6 | export default { 7 | // All imported modules in your tests should be mocked automatically 8 | // automock: false, 9 | 10 | // Stop running tests after `n` failures 11 | bail: true, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "C:\\Users\\Halisson\\AppData\\Local\\Temp\\jest", 15 | 16 | // Automatically clear mock calls and instances between every test 17 | clearMocks: true, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | // collectCoverage: false, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: undefined, 24 | 25 | // The directory where Jest should output its coverage files 26 | // coverageDirectory: undefined, 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | // coveragePathIgnorePatterns: [ 30 | // "\\\\node_modules\\\\" 31 | // ], 32 | 33 | // Indicates which provider should be used to instrument code for coverage 34 | coverageProvider: "v8", 35 | 36 | // A list of reporter names that Jest uses when writing coverage reports 37 | // coverageReporters: [ 38 | // "json", 39 | // "text", 40 | // "lcov", 41 | // "clover" 42 | // ], 43 | 44 | // An object that configures minimum threshold enforcement for coverage results 45 | // coverageThreshold: undefined, 46 | 47 | // A path to a custom dependency extractor 48 | // dependencyExtractor: undefined, 49 | 50 | // Make calling deprecated APIs throw helpful error messages 51 | // errorOnDeprecated: false, 52 | 53 | // Force coverage collection from ignored files using an array of glob patterns 54 | // forceCoverageMatch: [], 55 | 56 | // A path to a module which exports an async function that is triggered once before all test suites 57 | // globalSetup: undefined, 58 | 59 | // A path to a module which exports an async function that is triggered once after all test suites 60 | // globalTeardown: undefined, 61 | 62 | // A set of global variables that need to be available in all test environments 63 | // globals: {}, 64 | 65 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 66 | // maxWorkers: "50%", 67 | 68 | // An array of directory names to be searched recursively up from the requiring module's location 69 | // moduleDirectories: [ 70 | // "node_modules" 71 | // ], 72 | 73 | // An array of file extensions your modules use 74 | // moduleFileExtensions: [ 75 | // "js", 76 | // "json", 77 | // "jsx", 78 | // "ts", 79 | // "tsx", 80 | // "node" 81 | // ], 82 | 83 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 84 | // moduleNameMapper: {}, 85 | 86 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 87 | // modulePathIgnorePatterns: [], 88 | 89 | // Activates notifications for test results 90 | // notify: false, 91 | 92 | // An enum that specifies notification mode. Requires { notify: true } 93 | // notifyMode: "failure-change", 94 | 95 | // A preset that is used as a base for Jest's configuration 96 | preset: "ts-jest", 97 | 98 | // Run tests from one or more projects 99 | // projects: undefined, 100 | 101 | // Use this configuration option to add custom reporters to Jest 102 | // reporters: undefined, 103 | 104 | // Automatically reset mock state between every test 105 | // resetMocks: false, 106 | 107 | // Reset the module registry before running each individual test 108 | // resetModules: false, 109 | 110 | // A path to a custom resolver 111 | // resolver: undefined, 112 | 113 | // Automatically restore mock state between every test 114 | // restoreMocks: false, 115 | 116 | // The root directory that Jest should scan for tests and modules within 117 | // rootDir: undefined, 118 | 119 | // A list of paths to directories that Jest should use to search for files in 120 | // roots: [ 121 | // "" 122 | // ], 123 | 124 | // Allows you to use a custom runner instead of Jest's default test runner 125 | // runner: "jest-runner", 126 | 127 | // The paths to modules that run some code to configure or set up the testing environment before each test 128 | // setupFiles: [], 129 | 130 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 131 | // setupFilesAfterEnv: [], 132 | 133 | // The number of seconds after which a test is considered as slow and reported as such in the results. 134 | // slowTestThreshold: 5, 135 | 136 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 137 | // snapshotSerializers: [], 138 | 139 | // The test environment that will be used for testing 140 | // testEnvironment: "node", 141 | 142 | // Options that will be passed to the testEnvironment 143 | // testEnvironmentOptions: {}, 144 | 145 | // Adds a location field to test results 146 | // testLocationInResults: false, 147 | 148 | // The glob patterns Jest uses to detect test files 149 | testMatch: ["**/__tests__/*.test.ts"], 150 | 151 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 152 | // testPathIgnorePatterns: [ 153 | // "\\\\node_modules\\\\" 154 | // ], 155 | 156 | // The regexp pattern or array of patterns that Jest uses to detect test files 157 | // testRegex: [], 158 | 159 | // This option allows the use of a custom results processor 160 | // testResultsProcessor: undefined, 161 | 162 | // This option allows use of a custom test runner 163 | // testRunner: "jasmine2", 164 | 165 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 166 | // testURL: "http://localhost", 167 | 168 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 169 | // timers: "real", 170 | 171 | // A map from regular expressions to paths to transformers 172 | // transform: undefined, 173 | 174 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 175 | // transformIgnorePatterns: [ 176 | // "\\\\node_modules\\\\", 177 | // "\\.pnp\\.[^\\\\]+$" 178 | // ], 179 | 180 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 181 | // unmockedModulePathPatterns: undefined, 182 | 183 | // Indicates whether each individual test should be reported during the run 184 | // verbose: undefined, 185 | 186 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 187 | // watchPathIgnorePatterns: [], 188 | 189 | // Whether to use watchman for file crawling 190 | // watchman: true, 191 | }; 192 | -------------------------------------------------------------------------------- /moveit/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 | -------------------------------------------------------------------------------- /rocketpay/.credo.exs: -------------------------------------------------------------------------------- 1 | # This file contains the configuration for Credo and you are probably reading 2 | # this after creating it with `mix credo.gen.config`. 3 | # 4 | # If you find anything wrong or unclear in this file, please report an 5 | # issue on GitHub: https://github.com/rrrene/credo/issues 6 | # 7 | %{ 8 | # 9 | # You can have as many configs as you like in the `configs:` field. 10 | configs: [ 11 | %{ 12 | # 13 | # Run any config using `mix credo -C `. If no config name is given 14 | # "default" is used. 15 | # 16 | name: "default", 17 | # 18 | # These are the files included in the analysis: 19 | files: %{ 20 | # 21 | # You can give explicit globs or simply directories. 22 | # In the latter case `**/*.{ex,exs}` will be used. 23 | # 24 | included: [ 25 | "lib/", 26 | "src/", 27 | "test/", 28 | "web/", 29 | "apps/*/lib/", 30 | "apps/*/src/", 31 | "apps/*/test/", 32 | "apps/*/web/" 33 | ], 34 | excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"] 35 | }, 36 | # 37 | # Load and configure plugins here: 38 | # 39 | plugins: [], 40 | # 41 | # If you create your own checks, you must specify the source files for 42 | # them here, so they can be loaded by Credo before running the analysis. 43 | # 44 | requires: [], 45 | # 46 | # If you want to enforce a style guide and need a more traditional linting 47 | # experience, you can change `strict` to `true` below: 48 | # 49 | strict: false, 50 | # 51 | # To modify the timeout for parsing files, change this value: 52 | # 53 | parse_timeout: 5000, 54 | # 55 | # If you want to use uncolored output by default, you can change `color` 56 | # to `false` below: 57 | # 58 | color: true, 59 | # 60 | # You can customize the parameters of any check by adding a second element 61 | # to the tuple. 62 | # 63 | # To disable a check put `false` as second element: 64 | # 65 | # {Credo.Check.Design.DuplicatedCode, false} 66 | # 67 | checks: [ 68 | # 69 | ## Consistency Checks 70 | # 71 | {Credo.Check.Consistency.ExceptionNames, []}, 72 | {Credo.Check.Consistency.LineEndings, []}, 73 | {Credo.Check.Consistency.ParameterPatternMatching, []}, 74 | {Credo.Check.Consistency.SpaceAroundOperators, []}, 75 | {Credo.Check.Consistency.SpaceInParentheses, []}, 76 | {Credo.Check.Consistency.TabsOrSpaces, []}, 77 | 78 | # 79 | ## Design Checks 80 | # 81 | # You can customize the priority of any check 82 | # Priority values are: `low, normal, high, higher` 83 | # 84 | {Credo.Check.Design.AliasUsage, 85 | [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]}, 86 | # You can also customize the exit_status of each check. 87 | # If you don't want TODO comments to cause `mix credo` to fail, just 88 | # set this value to 0 (zero). 89 | # 90 | {Credo.Check.Design.TagTODO, [exit_status: 2]}, 91 | {Credo.Check.Design.TagFIXME, []}, 92 | 93 | # 94 | ## Readability Checks 95 | # 96 | {Credo.Check.Readability.AliasOrder, []}, 97 | {Credo.Check.Readability.FunctionNames, []}, 98 | {Credo.Check.Readability.LargeNumbers, []}, 99 | {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 100]}, 100 | {Credo.Check.Readability.ModuleAttributeNames, []}, 101 | {Credo.Check.Readability.ModuleDoc, false}, 102 | {Credo.Check.Readability.ModuleNames, []}, 103 | {Credo.Check.Readability.ParenthesesInCondition, []}, 104 | {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []}, 105 | {Credo.Check.Readability.PredicateFunctionNames, []}, 106 | {Credo.Check.Readability.PreferImplicitTry, []}, 107 | {Credo.Check.Readability.RedundantBlankLines, []}, 108 | {Credo.Check.Readability.Semicolons, []}, 109 | {Credo.Check.Readability.SpaceAfterCommas, []}, 110 | {Credo.Check.Readability.StringSigils, []}, 111 | {Credo.Check.Readability.TrailingBlankLine, []}, 112 | {Credo.Check.Readability.TrailingWhiteSpace, []}, 113 | {Credo.Check.Readability.UnnecessaryAliasExpansion, []}, 114 | {Credo.Check.Readability.VariableNames, []}, 115 | 116 | # 117 | ## Refactoring Opportunities 118 | # 119 | {Credo.Check.Refactor.CondStatements, []}, 120 | {Credo.Check.Refactor.CyclomaticComplexity, []}, 121 | {Credo.Check.Refactor.FunctionArity, []}, 122 | {Credo.Check.Refactor.LongQuoteBlocks, []}, 123 | # {Credo.Check.Refactor.MapInto, []}, 124 | {Credo.Check.Refactor.MatchInCondition, []}, 125 | {Credo.Check.Refactor.NegatedConditionsInUnless, []}, 126 | {Credo.Check.Refactor.NegatedConditionsWithElse, []}, 127 | {Credo.Check.Refactor.Nesting, []}, 128 | {Credo.Check.Refactor.UnlessWithElse, []}, 129 | {Credo.Check.Refactor.WithClauses, []}, 130 | 131 | # 132 | ## Warnings 133 | # 134 | {Credo.Check.Warning.ApplicationConfigInModuleAttribute, []}, 135 | {Credo.Check.Warning.BoolOperationOnSameValues, []}, 136 | {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, 137 | {Credo.Check.Warning.IExPry, []}, 138 | {Credo.Check.Warning.IoInspect, []}, 139 | # {Credo.Check.Warning.LazyLogging, []}, 140 | {Credo.Check.Warning.MixEnv, false}, 141 | {Credo.Check.Warning.OperationOnSameValues, []}, 142 | {Credo.Check.Warning.OperationWithConstantResult, []}, 143 | {Credo.Check.Warning.RaiseInsideRescue, []}, 144 | {Credo.Check.Warning.UnusedEnumOperation, []}, 145 | {Credo.Check.Warning.UnusedFileOperation, []}, 146 | {Credo.Check.Warning.UnusedKeywordOperation, []}, 147 | {Credo.Check.Warning.UnusedListOperation, []}, 148 | {Credo.Check.Warning.UnusedPathOperation, []}, 149 | {Credo.Check.Warning.UnusedRegexOperation, []}, 150 | {Credo.Check.Warning.UnusedStringOperation, []}, 151 | {Credo.Check.Warning.UnusedTupleOperation, []}, 152 | {Credo.Check.Warning.UnsafeExec, []}, 153 | 154 | # 155 | # Checks scheduled for next check update (opt-in for now, just replace `false` with `[]`) 156 | 157 | # 158 | # Controversial and experimental checks (opt-in, just replace `false` with `[]`) 159 | # 160 | {Credo.Check.Consistency.MultiAliasImportRequireUse, false}, 161 | {Credo.Check.Consistency.UnusedVariableNames, false}, 162 | {Credo.Check.Design.DuplicatedCode, false}, 163 | {Credo.Check.Readability.AliasAs, false}, 164 | {Credo.Check.Readability.BlockPipe, false}, 165 | {Credo.Check.Readability.ImplTrue, false}, 166 | {Credo.Check.Readability.MultiAlias, false}, 167 | {Credo.Check.Readability.SeparateAliasRequire, false}, 168 | {Credo.Check.Readability.SinglePipe, false}, 169 | {Credo.Check.Readability.Specs, false}, 170 | {Credo.Check.Readability.StrictModuleLayout, false}, 171 | {Credo.Check.Readability.WithCustomTaggedTuple, false}, 172 | {Credo.Check.Refactor.ABCSize, false}, 173 | {Credo.Check.Refactor.AppendSingleItem, false}, 174 | {Credo.Check.Refactor.DoubleBooleanNegation, false}, 175 | {Credo.Check.Refactor.ModuleDependencies, false}, 176 | {Credo.Check.Refactor.NegatedIsNil, false}, 177 | {Credo.Check.Refactor.PipeChainStart, false}, 178 | {Credo.Check.Refactor.VariableRebinding, false}, 179 | {Credo.Check.Warning.LeakyEnvironment, false}, 180 | {Credo.Check.Warning.MapGetUnsafePass, false}, 181 | {Credo.Check.Warning.UnsafeToAtom, false} 182 | 183 | # 184 | # Custom checks can be created using `mix credo.gen.check`. 185 | # 186 | ] 187 | } 188 | ] 189 | } 190 | -------------------------------------------------------------------------------- /rocketpay/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.0", "6cb662d5c1b0a8858801cf20997bd006e7016aa8c52959c9ef80e0f34fb60b7a", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2c81d61d4f6ed0e5cf7bf27a9109b791ff216a1034b3d541327484f46dd43769"}, 3 | "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, 4 | "certifi": {:hex, :certifi, "2.5.3", "70bdd7e7188c804f3a30ee0e7c99655bc35d8ac41c23e12325f36ab449b70651", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "ed516acb3929b101208a9d700062d520f3953da3b6b918d866106ffa980e1c10"}, 5 | "comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"}, 6 | "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, 7 | "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"}, 8 | "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"}, 9 | "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"}, 10 | "credo": {:hex, :credo, "1.5.5", "e8f422026f553bc3bebb81c8e8bf1932f498ca03339856c7fec63d3faac8424b", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd8623ab7091956a855dc9f3062486add9c52d310dfd62748779c4315d8247de"}, 11 | "db_connection": {:hex, :db_connection, "2.3.1", "4c9f3ed1ef37471cbdd2762d6655be11e38193904d9c5c1c9389f1b891a3088e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "abaab61780dde30301d840417890bd9f74131041afd02174cf4e10635b3a63f5"}, 12 | "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, 13 | "ecto": {:hex, :ecto, "3.5.8", "8ebf12be6016cb99313348ba7bb4612f4114b9a506d6da79a2adc7ef449340bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ea0be182ea8922eb7742e3ae8e71b67ee00ae177de1bf76210299a5f16ba4c77"}, 14 | "ecto_sql": {:hex, :ecto_sql, "3.5.4", "a9e292c40bd79fff88885f95f1ecd7b2516e09aa99c7dd0201aa84c54d2358e4", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1fff1a28a898d7bbef263f1f3ea425b04ba9f33816d843238c84eff883347343"}, 15 | "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"}, 16 | "excoveralls": {:hex, :excoveralls, "0.14.0", "4b562d2acd87def01a3d1621e40037fdbf99f495ed3a8570dfcf1ab24e15f76d", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "94f17478b0cca020bcd85ce7eafea82d2856f7ed022be777734a2f864d36091a"}, 17 | "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, 18 | "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"}, 19 | "hackney": {:hex, :hackney, "1.17.0", "717ea195fd2f898d9fe9f1ce0afcc2621a41ecfe137fae57e7fe6e9484b9aa99", [:rebar3], [{:certifi, "~>2.5", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "64c22225f1ea8855f584720c0e5b3cd14095703af1c9fbc845ba042811dc671c"}, 20 | "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, 21 | "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, 22 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, 23 | "mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"}, 24 | "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, 25 | "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, 26 | "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.3.0", "929f34678198844a9f948157a5030b8279d5e3e6f672a38065f4ba3a793263bc", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "1e06f1aa09185e6b371a502adff72c28ecdc6b16d192cd6df148ce1cbd9b4abd"}, 27 | "phoenix": {:hex, :phoenix, "1.5.7", "2923bb3af924f184459fe4fa4b100bd25fa6468e69b2803dfae82698269aa5e0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "774cd64417c5a3788414fdbb2be2eb9bcd0c048d9e6ad11a0c1fd67b7c0d0978"}, 28 | "phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"}, 29 | "phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"}, 30 | "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.4.0", "87990e68b60213d7487e65814046f9a2bed4a67886c943270125913499b3e5c3", [:mix], [{:ecto_psql_extras, "~> 0.4.1 or ~> 0.5", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.14.1 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.15.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.4.0 or ~> 0.5.0 or ~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "8d52149e58188e9e4497cc0d8900ab94d9b66f96998ec38c47c7a4f8f4f50e57"}, 31 | "phoenix_live_view": {:hex, :phoenix_live_view, "0.15.4", "86908dc9603cc81c07e84725ee42349b5325cb250c9c20d3533856ff18dbb7dc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 0.5", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "35d78f3c35fe10a995dca5f4ab50165b7a90cbe02e23de245381558f821e9462"}, 32 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"}, 33 | "plug": {:hex, :plug, "1.11.0", "f17217525597628298998bc3baed9f8ea1fa3f1160aa9871aee6df47a6e4d38e", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d9c633f0499f9dc5c2fd069161af4e2e7756890b81adcbb2ceaa074e8308876"}, 34 | "plug_cowboy": {:hex, :plug_cowboy, "2.4.1", "779ba386c0915027f22e14a48919a9545714f849505fa15af2631a0d298abf0f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d72113b6dff7b37a7d9b2a5b68892808e3a9a752f2bf7e503240945385b70507"}, 35 | "plug_crypto": {:hex, :plug_crypto, "1.2.1", "5c854427528bf61d159855cedddffc0625e2228b5f30eff76d5a4de42d896ef4", [:mix], [], "hexpm", "6961c0e17febd9d0bfa89632d391d2545d2e0eb73768f5f50305a23961d8782c"}, 36 | "postgrex": {:hex, :postgrex, "0.15.8", "f5e782bbe5e8fa178d5e3cd1999c857dc48eda95f0a4d7f7bd92a50e84a0d491", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "698fbfacea34c4cf22c8281abeb5cf68d99628d541874f085520ab3b53d356fe"}, 37 | "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, 38 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, 39 | "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, 40 | "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.0", "da9d49ee7e6bb1c259d36ce6539cd45ae14d81247a2b0c90edf55e2b50507f7b", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5cfe67ad464b243835512aa44321cee91faed6ea868d7fb761d7016e02915c3d"}, 41 | "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"}, 42 | "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, 43 | } 44 | --------------------------------------------------------------------------------