1
Gemfile
1
Gemfile
@@ -20,6 +20,7 @@ gem "devise-i18n"
|
|||||||
gem "administrate-field-active_storage"
|
gem "administrate-field-active_storage"
|
||||||
gem "tailwindcss-rails"
|
gem "tailwindcss-rails"
|
||||||
gem "administrate"
|
gem "administrate"
|
||||||
|
gem "money-rails"
|
||||||
gem "enumerize"
|
gem "enumerize"
|
||||||
gem "graphql"
|
gem "graphql"
|
||||||
gem "pundit"
|
gem "pundit"
|
||||||
|
|||||||
10
Gemfile.lock
10
Gemfile.lock
@@ -157,6 +157,15 @@ GEM
|
|||||||
minitest (5.14.4)
|
minitest (5.14.4)
|
||||||
momentjs-rails (2.20.1)
|
momentjs-rails (2.20.1)
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
|
monetize (1.9.4)
|
||||||
|
money (~> 6.12)
|
||||||
|
money (6.13.8)
|
||||||
|
i18n (>= 0.6.4, <= 2)
|
||||||
|
money-rails (1.14.0)
|
||||||
|
activesupport (>= 3.0)
|
||||||
|
monetize (~> 1.9.0)
|
||||||
|
money (~> 6.13.2)
|
||||||
|
railties (>= 3.0)
|
||||||
msgpack (1.4.2)
|
msgpack (1.4.2)
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.12.1)
|
nokogiri (1.12.1)
|
||||||
@@ -330,6 +339,7 @@ DEPENDENCIES
|
|||||||
graphql_playground-rails
|
graphql_playground-rails
|
||||||
image_processing (~> 1.12)
|
image_processing (~> 1.12)
|
||||||
listen (~> 3.3)
|
listen (~> 3.3)
|
||||||
|
money-rails
|
||||||
pg (~> 1.1)
|
pg (~> 1.1)
|
||||||
pry-byebug
|
pry-byebug
|
||||||
puma (~> 5.0)
|
puma (~> 5.0)
|
||||||
|
|||||||
15
app/graphql/types/fiat_balance_type.rb
Normal file
15
app/graphql/types/fiat_balance_type.rb
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
module Types
|
||||||
|
class FiatBalanceType < Types::BaseObject
|
||||||
|
implements GraphQL::Types::Relay::Node
|
||||||
|
global_id_field :id
|
||||||
|
|
||||||
|
graphql_name "FiatBalance"
|
||||||
|
|
||||||
|
field :id, ID, null: false
|
||||||
|
field :amount_cents, Integer, null: false
|
||||||
|
field :amount_currency, String, null: false
|
||||||
|
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
|
||||||
|
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -13,5 +13,10 @@ module Types
|
|||||||
def balances
|
def balances
|
||||||
Pundit.policy_scope(current_user, Balance)
|
Pundit.policy_scope(current_user, Balance)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
field :fiat_balances, FiatBalanceType.connection_type, null: false
|
||||||
|
def fiat_balances
|
||||||
|
Pundit.policy_scope(current_user, FiatBalance)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ class XStakeSchema < GraphQL::Schema
|
|||||||
Types::CurrencyType
|
Types::CurrencyType
|
||||||
when Balance
|
when Balance
|
||||||
Types::BalanceType
|
Types::BalanceType
|
||||||
|
when FiatBalance
|
||||||
|
Types::FiatBalanceType
|
||||||
else
|
else
|
||||||
raise(GraphQL::RequiredImplementationMissingError, "Unexpected object: #{obj}")
|
raise(GraphQL::RequiredImplementationMissingError, "Unexpected object: #{obj}")
|
||||||
end
|
end
|
||||||
|
|||||||
71
app/javascript/__generated__/schema.graphql
generated
71
app/javascript/__generated__/schema.graphql
generated
@@ -1,5 +1,5 @@
|
|||||||
type Balance implements Node {
|
type Balance implements Node {
|
||||||
amount: Float!
|
amount: String!
|
||||||
currency: Currency!
|
currency: Currency!
|
||||||
id: ID!
|
id: ID!
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,54 @@ type Currency implements Node {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FiatBalance implements Node {
|
||||||
|
amountCents: Int!
|
||||||
|
amountCurrency: String!
|
||||||
|
createdAt: ISO8601DateTime!
|
||||||
|
id: ID!
|
||||||
|
updatedAt: ISO8601DateTime!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The connection type for FiatBalance.
|
||||||
|
"""
|
||||||
|
type FiatBalanceConnection {
|
||||||
|
"""
|
||||||
|
A list of edges.
|
||||||
|
"""
|
||||||
|
edges: [FiatBalanceEdge]
|
||||||
|
|
||||||
|
"""
|
||||||
|
A list of nodes.
|
||||||
|
"""
|
||||||
|
nodes: [FiatBalance]
|
||||||
|
|
||||||
|
"""
|
||||||
|
Information to aid in pagination.
|
||||||
|
"""
|
||||||
|
pageInfo: PageInfo!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
An edge in a connection.
|
||||||
|
"""
|
||||||
|
type FiatBalanceEdge {
|
||||||
|
"""
|
||||||
|
A cursor for use in pagination.
|
||||||
|
"""
|
||||||
|
cursor: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The item at the end of the edge.
|
||||||
|
"""
|
||||||
|
node: FiatBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
An ISO 8601-encoded datetime
|
||||||
|
"""
|
||||||
|
scalar ISO8601DateTime
|
||||||
|
|
||||||
"""
|
"""
|
||||||
An object with an ID.
|
An object with an ID.
|
||||||
"""
|
"""
|
||||||
@@ -102,6 +150,27 @@ type Query {
|
|||||||
last: Int
|
last: Int
|
||||||
): BalanceConnection!
|
): BalanceConnection!
|
||||||
currentUser: User
|
currentUser: User
|
||||||
|
fiatBalances(
|
||||||
|
"""
|
||||||
|
Returns the elements in the list that come after the specified cursor.
|
||||||
|
"""
|
||||||
|
after: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the elements in the list that come before the specified cursor.
|
||||||
|
"""
|
||||||
|
before: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the first _n_ elements from the list.
|
||||||
|
"""
|
||||||
|
first: Int
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the last _n_ elements from the list.
|
||||||
|
"""
|
||||||
|
last: Int
|
||||||
|
): FiatBalanceConnection!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Fetches an object given its ID.
|
Fetches an object given its ID.
|
||||||
|
|||||||
81
app/javascript/src/pages/Wallet/Balances.tsx
Normal file
81
app/javascript/src/pages/Wallet/Balances.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { graphql } from "babel-plugin-relay/macro";
|
||||||
|
import type { FC } from "react";
|
||||||
|
import React from "react";
|
||||||
|
import { useFragment } from "react-relay";
|
||||||
|
|
||||||
|
import { getCurrencyLogo } from "../../utils/getCurrencyLogo";
|
||||||
|
import type { Balances_balances$key } from "./__generated__/Balances_balances.graphql";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
balancesRef: Balances_balances$key;
|
||||||
|
};
|
||||||
|
export const Balances: FC<Props> = ({ balancesRef }) => {
|
||||||
|
const { nodes } = useFragment<Balances_balances$key>(
|
||||||
|
graphql`
|
||||||
|
fragment Balances_balances on BalanceConnection {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
amount
|
||||||
|
currency {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
balancesRef
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="-mx-4 sm:-mx-8 px-4 sm:px-8 py-4 overflow-x-auto">
|
||||||
|
<div className="inline-block min-w-full shadow rounded-lg overflow-hidden">
|
||||||
|
<table className="min-w-full leading-normal">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
||||||
|
>
|
||||||
|
Moeda
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
||||||
|
>
|
||||||
|
Saldo
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{nodes?.map((balance) => {
|
||||||
|
return (
|
||||||
|
<tr key={balance?.id}>
|
||||||
|
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<img
|
||||||
|
alt={`${balance?.currency.name} icon`}
|
||||||
|
src={getCurrencyLogo(balance?.currency.name)}
|
||||||
|
className="mx-auto object-cover rounded-full h-10 w-10 "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="ml-3">
|
||||||
|
<p className="text-gray-900 whitespace-no-wrap">
|
||||||
|
{balance?.currency.name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
||||||
|
<p className="text-gray-900 whitespace-no-wrap">
|
||||||
|
{balance?.amount}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
58
app/javascript/src/pages/Wallet/FiatBalances.tsx
Normal file
58
app/javascript/src/pages/Wallet/FiatBalances.tsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { graphql } from "babel-plugin-relay/macro";
|
||||||
|
import type { FC } from "react";
|
||||||
|
import React from "react";
|
||||||
|
import { useFragment } from "react-relay";
|
||||||
|
|
||||||
|
import type { FiatBalances_fiatBalances$key } from "./__generated__/FiatBalances_fiatBalances.graphql";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
fiatBalancesRef: FiatBalances_fiatBalances$key;
|
||||||
|
};
|
||||||
|
export const FiatBalances: FC<Props> = ({ fiatBalancesRef }) => {
|
||||||
|
const { nodes } = useFragment<FiatBalances_fiatBalances$key>(
|
||||||
|
graphql`
|
||||||
|
fragment FiatBalances_fiatBalances on FiatBalanceConnection {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
amountCents
|
||||||
|
amountCurrency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fiatBalancesRef
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!nodes?.length) return null;
|
||||||
|
|
||||||
|
const [firstResult] = nodes;
|
||||||
|
|
||||||
|
const amount = (
|
||||||
|
firstResult?.amountCents ? firstResult?.amountCents / 100 : 0
|
||||||
|
).toFixed(2);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="shadow rounded-lg p-4 bg-white dark:bg-gray-800">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="rounded-xl relative p-4 bg-green-200">
|
||||||
|
<svg
|
||||||
|
width="40"
|
||||||
|
fill="currentColor"
|
||||||
|
height="40"
|
||||||
|
className="text-green-500 h-4 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
|
viewBox="0 0 1792 1792"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M1362 1185q0 153-99.5 263.5t-258.5 136.5v175q0 14-9 23t-23 9h-135q-13 0-22.5-9.5t-9.5-22.5v-175q-66-9-127.5-31t-101.5-44.5-74-48-46.5-37.5-17.5-18q-17-21-2-41l103-135q7-10 23-12 15-2 24 9l2 2q113 99 243 125 37 8 74 8 81 0 142.5-43t61.5-122q0-28-15-53t-33.5-42-58.5-37.5-66-32-80-32.5q-39-16-61.5-25t-61.5-26.5-62.5-31-56.5-35.5-53.5-42.5-43.5-49-35.5-58-21-66.5-8.5-78q0-138 98-242t255-134v-180q0-13 9.5-22.5t22.5-9.5h135q14 0 23 9t9 23v176q57 6 110.5 23t87 33.5 63.5 37.5 39 29 15 14q17 18 5 38l-81 146q-8 15-23 16-14 3-27-7-3-3-14.5-12t-39-26.5-58.5-32-74.5-26-85.5-11.5q-95 0-155 43t-60 111q0 26 8.5 48t29.5 41.5 39.5 33 56 31 60.5 27 70 27.5q53 20 81 31.5t76 35 75.5 42.5 62 50 53 63.5 31.5 76.5 13 94z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<p className="text-md text-black dark:text-white ml-2">Saldo Fiat</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col justify-start">
|
||||||
|
<p className="text-gray-700 dark:text-gray-100 text-4xl text-left font-bold my-4">
|
||||||
|
{amount}
|
||||||
|
<span className="text-sm">{firstResult?.amountCurrency}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -3,21 +3,19 @@ import type { FC } from "react";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useLazyLoadQuery } from "react-relay";
|
import { useLazyLoadQuery } from "react-relay";
|
||||||
|
|
||||||
import { getCurrencyLogo } from "../../utils/getCurrencyLogo";
|
import { Balances } from "./Balances";
|
||||||
|
import { FiatBalances } from "./FiatBalances";
|
||||||
import type { WalletQuery } from "./__generated__/WalletQuery.graphql";
|
import type { WalletQuery } from "./__generated__/WalletQuery.graphql";
|
||||||
|
|
||||||
export const Wallet: FC = () => {
|
export const Wallet: FC = () => {
|
||||||
const { balances } = useLazyLoadQuery<WalletQuery>(
|
const { fiatBalances, balances } = useLazyLoadQuery<WalletQuery>(
|
||||||
graphql`
|
graphql`
|
||||||
query WalletQuery {
|
query WalletQuery {
|
||||||
|
fiatBalances {
|
||||||
|
...FiatBalances_fiatBalances
|
||||||
|
}
|
||||||
balances {
|
balances {
|
||||||
nodes {
|
...Balances_balances
|
||||||
id
|
|
||||||
amount
|
|
||||||
currency {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@@ -28,57 +26,8 @@ export const Wallet: FC = () => {
|
|||||||
<div className="flex flex-col h-full w-full overflow-x-hidden">
|
<div className="flex flex-col h-full w-full overflow-x-hidden">
|
||||||
<div className="container mx-auto px-4 sm:px-8 max-w-3xl">
|
<div className="container mx-auto px-4 sm:px-8 max-w-3xl">
|
||||||
<div className="py-8">
|
<div className="py-8">
|
||||||
<div className="-mx-4 sm:-mx-8 px-4 sm:px-8 py-4 overflow-x-auto">
|
<FiatBalances fiatBalancesRef={fiatBalances} />
|
||||||
<div className="inline-block min-w-full shadow rounded-lg overflow-hidden">
|
<Balances balancesRef={balances} />
|
||||||
<table className="min-w-full leading-normal">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
|
||||||
>
|
|
||||||
Moeda
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
|
||||||
>
|
|
||||||
Saldo
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{balances.nodes?.map((balance) => {
|
|
||||||
return (
|
|
||||||
<tr key={balance?.id}>
|
|
||||||
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-shrink-0">
|
|
||||||
<img
|
|
||||||
alt={`${balance?.currency.name} icon`}
|
|
||||||
src={getCurrencyLogo(balance?.currency.name)}
|
|
||||||
className="mx-auto object-cover rounded-full h-10 w-10 "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="ml-3">
|
|
||||||
<p className="text-gray-900 whitespace-no-wrap">
|
|
||||||
{balance?.currency.name}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
|
||||||
<p className="text-gray-900 whitespace-no-wrap">
|
|
||||||
{balance?.amount}
|
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
79
app/javascript/src/pages/Wallet/__generated__/Balances_balances.graphql.ts
generated
Normal file
79
app/javascript/src/pages/Wallet/__generated__/Balances_balances.graphql.ts
generated
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type Balances_balances = {
|
||||||
|
readonly nodes: ReadonlyArray<{
|
||||||
|
readonly id: string;
|
||||||
|
readonly amount: string;
|
||||||
|
readonly currency: {
|
||||||
|
readonly name: string;
|
||||||
|
};
|
||||||
|
} | null> | null;
|
||||||
|
readonly " $refType": "Balances_balances";
|
||||||
|
};
|
||||||
|
export type Balances_balances$data = Balances_balances;
|
||||||
|
export type Balances_balances$key = {
|
||||||
|
readonly " $data"?: Balances_balances$data;
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"Balances_balances">;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const node: ReaderFragment = {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "Balances_balances",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Balance",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "nodes",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Currency",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "currency",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "BalanceConnection",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
(node as any).hash = '2704da1dc9949b1becbd9ec947c5ec33';
|
||||||
|
export default node;
|
||||||
66
app/javascript/src/pages/Wallet/__generated__/FiatBalances_fiatBalances.graphql.ts
generated
Normal file
66
app/javascript/src/pages/Wallet/__generated__/FiatBalances_fiatBalances.graphql.ts
generated
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type FiatBalances_fiatBalances = {
|
||||||
|
readonly nodes: ReadonlyArray<{
|
||||||
|
readonly id: string;
|
||||||
|
readonly amountCents: number;
|
||||||
|
readonly amountCurrency: string;
|
||||||
|
} | null> | null;
|
||||||
|
readonly " $refType": "FiatBalances_fiatBalances";
|
||||||
|
};
|
||||||
|
export type FiatBalances_fiatBalances$data = FiatBalances_fiatBalances;
|
||||||
|
export type FiatBalances_fiatBalances$key = {
|
||||||
|
readonly " $data"?: FiatBalances_fiatBalances$data;
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"FiatBalances_fiatBalances">;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const node: ReaderFragment = {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "FiatBalances_fiatBalances",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "FiatBalance",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "nodes",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amountCurrency",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "FiatBalanceConnection",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
(node as any).hash = '0584f36abe6ca6f8612b8c593c4cfb6d';
|
||||||
|
export default node;
|
||||||
@@ -3,16 +3,14 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { ConcreteRequest } from "relay-runtime";
|
import { ConcreteRequest } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
export type WalletQueryVariables = {};
|
export type WalletQueryVariables = {};
|
||||||
export type WalletQueryResponse = {
|
export type WalletQueryResponse = {
|
||||||
readonly balances: {
|
readonly fiatBalances: {
|
||||||
readonly nodes: ReadonlyArray<{
|
readonly " $fragmentRefs": FragmentRefs<"FiatBalances_fiatBalances">;
|
||||||
readonly id: string;
|
|
||||||
readonly amount: number;
|
|
||||||
readonly currency: {
|
|
||||||
readonly name: string;
|
|
||||||
};
|
};
|
||||||
} | null> | null;
|
readonly balances: {
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"Balances_balances">;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type WalletQuery = {
|
export type WalletQuery = {
|
||||||
@@ -24,7 +22,15 @@ export type WalletQuery = {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
query WalletQuery {
|
query WalletQuery {
|
||||||
|
fiatBalances {
|
||||||
|
...FiatBalances_fiatBalances
|
||||||
|
}
|
||||||
balances {
|
balances {
|
||||||
|
...Balances_balances
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment Balances_balances on BalanceConnection {
|
||||||
nodes {
|
nodes {
|
||||||
id
|
id
|
||||||
amount
|
amount
|
||||||
@@ -34,6 +40,13 @@ query WalletQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fragment FiatBalances_fiatBalances on FiatBalanceConnection {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
amountCents
|
||||||
|
amountCurrency
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -44,20 +57,6 @@ var v0 = {
|
|||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
|
||||||
v1 = {
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "amount",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
v2 = {
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "name",
|
|
||||||
"storageKey": null
|
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
"fragment": {
|
"fragment": {
|
||||||
@@ -66,6 +65,22 @@ return {
|
|||||||
"metadata": null,
|
"metadata": null,
|
||||||
"name": "WalletQuery",
|
"name": "WalletQuery",
|
||||||
"selections": [
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "FiatBalanceConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "fiatBalances",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"args": null,
|
||||||
|
"kind": "FragmentSpread",
|
||||||
|
"name": "FiatBalances_fiatBalances"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -75,29 +90,9 @@ return {
|
|||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
"args": null,
|
||||||
"concreteType": "Balance",
|
"kind": "FragmentSpread",
|
||||||
"kind": "LinkedField",
|
"name": "Balances_balances"
|
||||||
"name": "nodes",
|
|
||||||
"plural": true,
|
|
||||||
"selections": [
|
|
||||||
(v0/*: any*/),
|
|
||||||
(v1/*: any*/),
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"concreteType": "Currency",
|
|
||||||
"kind": "LinkedField",
|
|
||||||
"name": "currency",
|
|
||||||
"plural": false,
|
|
||||||
"selections": [
|
|
||||||
(v2/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -112,6 +107,43 @@ return {
|
|||||||
"kind": "Operation",
|
"kind": "Operation",
|
||||||
"name": "WalletQuery",
|
"name": "WalletQuery",
|
||||||
"selections": [
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "FiatBalanceConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "fiatBalances",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "FiatBalance",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "nodes",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amountCurrency",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -129,7 +161,13 @@ return {
|
|||||||
"plural": true,
|
"plural": true,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v0/*: any*/),
|
(v0/*: any*/),
|
||||||
(v1/*: any*/),
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -138,7 +176,13 @@ return {
|
|||||||
"name": "currency",
|
"name": "currency",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v2/*: any*/),
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
(v0/*: any*/)
|
(v0/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -152,14 +196,14 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "6b8d0c664bd2d9df4d323c19c4a823a5",
|
"cacheID": "82d013e2bf418b53aeec5412f2f92661",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "WalletQuery",
|
"name": "WalletQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query WalletQuery {\n balances {\n nodes {\n id\n amount\n currency {\n name\n id\n }\n }\n }\n}\n"
|
"text": "query WalletQuery {\n fiatBalances {\n ...FiatBalances_fiatBalances\n }\n balances {\n ...Balances_balances\n }\n}\n\nfragment Balances_balances on BalanceConnection {\n nodes {\n id\n amount\n currency {\n name\n id\n }\n }\n}\n\nfragment FiatBalances_fiatBalances on FiatBalanceConnection {\n nodes {\n id\n amountCents\n amountCurrency\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
(node as any).hash = '428f4f1ab769f9056dd38ec641a30733';
|
(node as any).hash = '855efce679c691a77938b64376a1a805';
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
28
app/models/fiat_balance.rb
Normal file
28
app/models/fiat_balance.rb
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: fiat_balances
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# amount_cents :integer default(0), not null
|
||||||
|
# amount_currency :string default("BRL"), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_fiat_balances_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
|
#
|
||||||
|
class FiatBalance < ApplicationRecord
|
||||||
|
belongs_to :user
|
||||||
|
|
||||||
|
monetize :amount_cents
|
||||||
|
|
||||||
|
validates :amount, presence: true
|
||||||
|
end
|
||||||
@@ -26,6 +26,7 @@ class User < ApplicationRecord
|
|||||||
|
|
||||||
has_many :documents, class_name: "UserDocument", dependent: :destroy
|
has_many :documents, class_name: "UserDocument", dependent: :destroy
|
||||||
has_many :balances, dependent: :restrict_with_error
|
has_many :balances, dependent: :restrict_with_error
|
||||||
|
has_one :fiat_balance, dependent: :restrict_with_error
|
||||||
|
|
||||||
validates :first_name, :last_name, :email, presence: true
|
validates :first_name, :last_name, :email, presence: true
|
||||||
validates :email, uniqueness: true
|
validates :email, uniqueness: true
|
||||||
|
|||||||
10
app/policies/fiat_balance_policy.rb
Normal file
10
app/policies/fiat_balance_policy.rb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class FiatBalancePolicy < ApplicationPolicy
|
||||||
|
class Scope < Scope
|
||||||
|
def resolve
|
||||||
|
return scope.none if user.nil?
|
||||||
|
|
||||||
|
scope.where(user_id: user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
7
config/initializers/money.rb
Normal file
7
config/initializers/money.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
MoneyRails.configure do |config|
|
||||||
|
config.default_currency = :brl
|
||||||
|
config.rounding_mode = BigDecimal::ROUND_HALF_UP
|
||||||
|
config.locale_backend = :i18n
|
||||||
|
end
|
||||||
11
db/migrate/20210812011039_create_fiat_balances.rb
Normal file
11
db/migrate/20210812011039_create_fiat_balances.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
class CreateFiatBalances < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
create_table(:fiat_balances) do |t|
|
||||||
|
t.references(:user, null: false, foreign_key: true)
|
||||||
|
t.monetize(:amount)
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
12
db/schema.rb
generated
12
db/schema.rb
generated
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2021_08_11_121726) do
|
ActiveRecord::Schema.define(version: 2021_08_12_011039) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@@ -71,6 +71,15 @@ ActiveRecord::Schema.define(version: 2021_08_11_121726) do
|
|||||||
t.datetime "updated_at", precision: 6, null: false
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "fiat_balances", force: :cascade do |t|
|
||||||
|
t.bigint "user_id", null: false
|
||||||
|
t.integer "amount_cents", default: 0, null: false
|
||||||
|
t.string "amount_currency", default: "BRL", null: false
|
||||||
|
t.datetime "created_at", precision: 6, null: false
|
||||||
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
|
t.index ["user_id"], name: "index_fiat_balances_on_user_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "user_documents", force: :cascade do |t|
|
create_table "user_documents", force: :cascade do |t|
|
||||||
t.string "status", null: false
|
t.string "status", null: false
|
||||||
t.bigint "user_id", null: false
|
t.bigint "user_id", null: false
|
||||||
@@ -97,5 +106,6 @@ ActiveRecord::Schema.define(version: 2021_08_11_121726) do
|
|||||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||||
add_foreign_key "balances", "currencies"
|
add_foreign_key "balances", "currencies"
|
||||||
add_foreign_key "balances", "users"
|
add_foreign_key "balances", "users"
|
||||||
|
add_foreign_key "fiat_balances", "users"
|
||||||
add_foreign_key "user_documents", "users"
|
add_foreign_key "user_documents", "users"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -214,3 +214,5 @@ currencies.each do |currency|
|
|||||||
amount: random_floating_number
|
amount: random_floating_number
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
FiatBalance.create(user_id: user.id)
|
||||||
|
|||||||
26
spec/models/fiat_balance_spec.rb
Normal file
26
spec/models/fiat_balance_spec.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: fiat_balances
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# amount_cents :integer default(0), not null
|
||||||
|
# amount_currency :string default("BRL"), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# user_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_fiat_balances_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
|
#
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
RSpec.describe(FiatBalance, type: :model) do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
6
spec/policies/fiat_balance_policy_spec.rb
Normal file
6
spec/policies/fiat_balance_policy_spec.rb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
RSpec.describe(FiatBalancePolicy, type: :policy) do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user