Account page revised transactions list #14
pull/133/head
Jimmy Lauzau 7 years ago committed by GitHub
commit a275393500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 138
      apps/explorer/lib/explorer/chain.ex
  2. 384
      apps/explorer/test/explorer/chain_test.exs
  3. 26
      apps/explorer_web/assets/css/_typography.scss
  4. 18
      apps/explorer_web/assets/css/_utilities.scss
  5. 111
      apps/explorer_web/assets/css/_variables.scss
  6. 23
      apps/explorer_web/assets/css/app.scss
  7. 2
      apps/explorer_web/assets/css/components/_container.scss
  8. 68
      apps/explorer_web/assets/css/explorer/_button.scss
  9. 4
      apps/explorer_web/assets/css/explorer/_filter.scss
  10. 430
      apps/explorer_web/assets/css/explorer/_sb-admin-2.scss
  11. 3
      apps/explorer_web/assets/css/theme/_fonts.scss
  12. 928
      apps/explorer_web/assets/css/theme/_neutral-variables.scss
  13. 1
      apps/explorer_web/assets/css/theme/_variables.scss
  14. 11
      apps/explorer_web/lib/explorer_web/controllers/address_controller.ex
  15. 45
      apps/explorer_web/lib/explorer_web/controllers/address_transaction_controller.ex
  16. 31
      apps/explorer_web/lib/explorer_web/controllers/address_transaction_from_controller.ex
  17. 31
      apps/explorer_web/lib/explorer_web/controllers/address_transaction_to_controller.ex
  18. 13
      apps/explorer_web/lib/explorer_web/router.ex
  19. 38
      apps/explorer_web/lib/explorer_web/templates/address/show.html.eex
  20. 96
      apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex
  21. 108
      apps/explorer_web/lib/explorer_web/templates/address_transaction_from/index.html.eex
  22. 8
      apps/explorer_web/lib/explorer_web/views/address_transaction_from_view.ex
  23. 8
      apps/explorer_web/lib/explorer_web/views/address_transaction_to_view.ex
  24. 19
      apps/explorer_web/lib/explorer_web/views/address_transaction_view.ex
  25. 17
      apps/explorer_web/lib/explorer_web/views/address_view.ex
  26. 12
      apps/explorer_web/lib/explorer_web/views/transaction_view.ex
  27. 63
      apps/explorer_web/priv/gettext/default.pot
  28. 63
      apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po
  29. 12
      apps/explorer_web/test/explorer_web/controllers/address_controller_test.exs
  30. 16
      apps/explorer_web/test/explorer_web/controllers/address_transaction_controller_test.exs
  31. 92
      apps/explorer_web/test/explorer_web/controllers/address_transaction_from_controller_test.exs
  32. 247
      apps/explorer_web/test/explorer_web/features/contributor_browsing_test.exs
  33. 60
      apps/explorer_web/test/explorer_web/views/address_transaction_view_test.exs

@ -3,7 +3,7 @@ defmodule Explorer.Chain do
The chain context.
"""
import Ecto.Query, only: [from: 2, order_by: 2, preload: 2, where: 2, where: 3]
import Ecto.Query, only: [from: 2, or_where: 3, order_by: 2, preload: 2, where: 2]
alias Explorer.Chain.{
Address,
@ -42,11 +42,48 @@ defmodule Explorer.Chain do
"""
@type pagination :: map()
@typep direction_option :: :to | :from
@typep necessity_by_association_option :: {:necessity_by_association, necessity_by_association}
@typep pagination_option :: {:pagination, pagination}
# Functions
@doc """
`t:Explorer.Chain.Transaction/0`s from `address`.
## Options
* `:direction` - if specified, will filter transactions by address type. If `:to` is specified, only transactions
where the "to" address matches will be returned. Likewise, if `:from` is specified, only transactions where the
"from" address matches will be returned. If :direction is omitted, transactions either to or from the address
will be returned.
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.Transaction.t/0` has no associated record for that association, then the
`t:Explorer.Chain.Transaction.t/0` will not be included in the page `entries`.
* `:pagination` - pagination params to pass to scrivener.
"""
@spec address_to_transactions(Address.t(), [
direction_option | necessity_by_association_option | pagination_option
]) :: %Scrivener.Page{entries: [Transaction.t()]}
def address_to_transactions(address = %Address{}, options \\ [])
when is_list(options) do
address_id_to_transactions(address.id, options)
end
@doc """
The `t:Explorer.Chain.Address.t/0` `balance` in `unit`.
"""
@spec balance(Address.t(), :wei) :: Wei.t() | nil
@spec balance(Address.t(), :gwei) :: Wei.gwei() | nil
@spec balance(Address.t(), :ether) :: Wei.ether() | nil
def balance(%Address{balance: balance}, unit) do
case balance do
nil -> nil
_ -> Wei.to(balance, unit)
end
end
@doc """
Finds all `t:Explorer.Chain.Transaction.t/0` in the `t:Explorer.Chain.Block.t/0`.
@ -155,22 +192,48 @@ defmodule Explorer.Chain do
end
@doc """
`t:Explorer.Chain.Transaction/0`s from `address`.
The fee a `transaction` paid for the `t:Explorer.Transaction.t/0` `gas`
## Options
If the transaction is pending, then the fee will be a range of `unit`
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.Transaction.t/0` has no associated record for that association, then the
`t:Explorer.Chain.Transaction.t/0` will not be included in the page `entries`.
* `:pagination` - pagination params to pass to scrivener.
iex> Explorer.Chain.fee(
...> %Explorer.Chain.Transaction{gas: Decimal.new(3), gas_price: Decimal.new(2), receipt: nil},
...> :wei
...> )
{:maximum, Decimal.new(6)}
If the transaction has been confirmed in block, then the fee will be the actual fee paid in `unit` for the `gas_used`
in the `receipt`.
iex> Explorer.Chain.fee(
...> %Explorer.Chain.Transaction{
...> gas: Decimal.new(3),
...> gas_price: Decimal.new(2),
...> receipt: Explorer.Chain.Receipt{gas_used: Decimal.new(2)}
...> },
...> :wei
...> )
{:actual, Decimal.new(4)}
"""
@spec from_address_to_transactions(Address.t(), [
necessity_by_association_option | pagination_option
]) :: %Scrivener.Page{entries: [Transaction.t()]}
def from_address_to_transactions(address = %Address{}, options \\ [])
when is_list(options) do
address_to_transactions(address, Keyword.put(options, :direction, :from))
@spec fee(%Transaction{receipt: nil}, :ether | :gwei | :wei) :: {:maximum, Decimal.t()}
def fee(%Transaction{gas: gas, gas_price: gas_price, receipt: nil}, unit) do
fee =
gas
|> Decimal.mult(gas_price)
|> Wei.to(unit)
{:maximum, fee}
end
@spec fee(%Transaction{receipt: Receipt.t()}, :ether | :gwei | :wei) :: {:actual, Decimal.t()}
def fee(%Transaction{gas_price: gas_price, receipt: %Receipt{gas_used: gas_used}}, unit) do
fee =
gas_used
|> Decimal.mult(gas_price)
|> Wei.to(unit)
{:actual, fee}
end
@doc """
@ -335,24 +398,6 @@ defmodule Explorer.Chain do
end
end
@doc """
`t:Explorer.Chain.Transaction/0`s to `address`.
## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.Transaction.t/0` has no associated record for that association, then the
`t:Explorer.Chain.Transaction.t/0` will not be included in the page `entries`.
* `:pagination` - pagination params to pass to scrivener.
"""
@spec to_address_to_transactions(Address.t(), [
necessity_by_association_option | pagination_option
]) :: %Scrivener.Page{entries: [Transaction.t()]}
def to_address_to_transactions(address = %Address{}, options \\ []) when is_list(options) do
address_to_transactions(address, Keyword.put(options, :direction, :to))
end
@doc """
Count of `t:Explorer.Chain.Transaction.t/0`.
@ -499,10 +544,11 @@ defmodule Explorer.Chain do
defp address_id_to_transactions(address_id, named_arguments)
when is_integer(address_id) and is_list(named_arguments) do
field =
case Keyword.fetch!(named_arguments, :direction) do
:to -> :to_address_id
:from -> :from_address_id
address_fields =
case Keyword.get(named_arguments, :direction) do
:to -> [:to_address_id]
:from -> [:from_address_id]
nil -> [:to_address_id, :from_address_id]
end
necessity_by_association = Keyword.get(named_arguments, :necessity_by_association, %{})
@ -510,19 +556,11 @@ defmodule Explorer.Chain do
Transaction
|> join_associations(necessity_by_association)
|> chronologically()
|> where([t], field(t, ^field) == ^address_id)
|> reverse_chronologically()
|> where_address_fields_match(address_fields, address_id)
|> Repo.paginate(pagination)
end
defp address_to_transactions(%Address{id: address_id}, options) when is_list(options) do
address_id_to_transactions(address_id, options)
end
defp chronologically(query) do
from(q in query, order_by: [desc: q.inserted_at, desc: q.id])
end
defp for_parent_transaction(query, hash) when is_binary(hash) do
from(
child in query,
@ -556,6 +594,10 @@ defmodule Explorer.Chain do
)
end
defp reverse_chronologically(query) do
from(q in query, order_by: [desc: q.inserted_at, desc: q.id])
end
defp transaction_hash_to_logs(transaction_hash, options)
when is_binary(transaction_hash) and is_list(options) do
lower_transaction_hash = String.downcase(transaction_hash)
@ -575,6 +617,12 @@ defmodule Explorer.Chain do
|> Repo.paginate(pagination)
end
defp where_address_fields_match(query, address_fields, address_id) do
Enum.reduce(address_fields, query, fn field, query ->
or_where(query, [t], field(t, ^field) == ^address_id)
end)
end
defp where_hash(query, hash) do
from(
q in query,

@ -12,6 +12,166 @@ defmodule Explorer.ChainTest do
# Tests
describe "address_to_transactions/2" do
test "without transactions" do
address = insert(:address)
assert Repo.aggregate(Transaction, :count, :id) == 0
assert %Scrivener.Page{
entries: [],
page_number: 1,
total_entries: 0
} = Chain.address_to_transactions(address)
end
test "with from transactions" do
%Transaction{from_address_id: from_address_id, id: transaction_id} = insert(:transaction)
address = Repo.get!(Address, from_address_id)
assert %Scrivener.Page{
entries: [%Transaction{id: ^transaction_id}],
page_number: 1,
total_entries: 1
} = Chain.address_to_transactions(address, direction: :from)
end
test "with to transactions" do
%Transaction{to_address_id: to_address_id, id: transaction_id} = insert(:transaction)
address = Repo.get!(Address, to_address_id)
assert %Scrivener.Page{
entries: [%Transaction{id: ^transaction_id}],
page_number: 1,
total_entries: 1
} = Chain.address_to_transactions(address, direction: :to)
end
test "with to and from transactions and direction: :from" do
%Transaction{from_address_id: address_id, id: from_transaction_id} = insert(:transaction)
%Transaction{} = insert(:transaction, to_address_id: address_id)
address = Repo.get!(Address, address_id)
# only contains "from" transaction
assert %Scrivener.Page{
entries: [%Transaction{id: ^from_transaction_id}],
page_number: 1,
total_entries: 1
} = Chain.address_to_transactions(address, direction: :from)
end
test "with to and from transactions and direction: :to" do
%Transaction{from_address_id: address_id} = insert(:transaction)
%Transaction{id: to_transaction_id} = insert(:transaction, to_address_id: address_id)
address = Repo.get!(Address, address_id)
# only contains "to" transaction
assert %Scrivener.Page{
entries: [%Transaction{id: ^to_transaction_id}],
page_number: 1,
total_entries: 1
} = Chain.address_to_transactions(address, direction: :to)
end
test "with to and from transactions and no :direction option" do
%Transaction{from_address_id: address_id, id: from_transaction_id} = insert(:transaction)
%Transaction{id: to_transaction_id} = insert(:transaction, to_address_id: address_id)
address = Repo.get!(Address, address_id)
# only contains "to" transaction
assert %Scrivener.Page{
entries: [
%Transaction{id: ^to_transaction_id},
%Transaction{id: ^from_transaction_id}
],
page_number: 1,
total_entries: 2
} = Chain.address_to_transactions(address)
end
test "with transactions with receipt required without receipt does not return transaction" do
address = %Address{id: to_address_id} = insert(:address)
%Transaction{id: transaction_id_with_receipt} = insert(:transaction, to_address_id: to_address_id)
insert(:receipt, transaction_id: transaction_id_with_receipt)
%Transaction{id: transaction_id_without_receipt} = insert(:transaction, to_address_id: to_address_id)
assert %Scrivener.Page{
entries: [%Transaction{id: ^transaction_id_with_receipt, receipt: %Receipt{}}],
page_number: 1,
total_entries: 1
} =
Chain.address_to_transactions(
address,
necessity_by_association: %{receipt: :required}
)
assert %Scrivener.Page{
entries: transactions,
page_number: 1,
total_entries: 2
} =
Chain.address_to_transactions(
address,
necessity_by_association: %{receipt: :optional}
)
assert length(transactions) == 2
transaction_by_id =
Enum.into(transactions, %{}, fn transaction = %Transaction{id: id} ->
{id, transaction}
end)
assert %Transaction{receipt: %Receipt{}} = transaction_by_id[transaction_id_with_receipt]
assert %Transaction{receipt: nil} = transaction_by_id[transaction_id_without_receipt]
end
test "with transactions can be paginated" do
adddress = %Address{id: to_address_id} = insert(:address)
transactions = insert_list(2, :transaction, to_address_id: to_address_id)
[%Transaction{id: oldest_transaction_id}, %Transaction{id: newest_transaction_id}] = transactions
assert %Scrivener.Page{
entries: [%Transaction{id: ^newest_transaction_id}],
page_number: 1,
page_size: 1,
total_entries: 2,
total_pages: 2
} = Chain.address_to_transactions(adddress, pagination: %{page_size: 1})
assert %Scrivener.Page{
entries: [%Transaction{id: ^oldest_transaction_id}],
page_number: 2,
page_size: 1,
total_entries: 2,
total_pages: 2
} = Chain.address_to_transactions(adddress, pagination: %{page: 2, page_size: 1})
end
end
describe "balance/2" do
test "with Address.t with :wei" do
assert Chain.balance(%Address{balance: Decimal.new(1)}, :wei) == Decimal.new(1)
assert Chain.balance(%Address{balance: nil}, :wei) == nil
end
test "with Address.t with :gwei" do
assert Chain.balance(%Address{balance: Decimal.new(1)}, :gwei) == Decimal.new("1e-9")
assert Chain.balance(%Address{balance: Decimal.new("1e9")}, :gwei) == Decimal.new(1)
assert Chain.balance(%Address{balance: nil}, :gwei) == nil
end
test "with Address.t with :ether" do
assert Chain.balance(%Address{balance: Decimal.new(1)}, :ether) == Decimal.new("1e-18")
assert Chain.balance(%Address{balance: Decimal.new("1e18")}, :ether) == Decimal.new(1)
assert Chain.balance(%Address{balance: nil}, :ether) == nil
end
end
describe "block_to_transactions/1" do
test "without transactions" do
block = insert(:block)
@ -180,109 +340,71 @@ defmodule Explorer.ChainTest do
end
end
describe "gas_price/2" do
test ":wei unit" do
assert Chain.gas_price(%Transaction{gas_price: Decimal.new(1)}, :wei) == Decimal.new(1)
describe "fee/2" do
test "without receipt with :wei unit" do
assert Chain.fee(%Transaction{gas: Decimal.new(3), gas_price: Decimal.new(2), receipt: nil}, :wei) ==
{:maximum, Decimal.new(6)}
end
test ":gwei unit" do
assert Chain.gas_price(%Transaction{gas_price: Decimal.new(1)}, :gwei) == Decimal.new("1e-9")
assert Chain.gas_price(%Transaction{gas_price: Decimal.new("1e9")}, :gwei) == Decimal.new(1)
test "without receipt with :gwei unit" do
assert Chain.fee(%Transaction{gas: Decimal.new(3), gas_price: Decimal.new(2), receipt: nil}, :gwei) ==
{:maximum, Decimal.new("6e-9")}
end
test ":ether unit" do
assert Chain.gas_price(%Transaction{gas_price: Decimal.new(1)}, :ether) == Decimal.new("1e-18")
assert Chain.gas_price(%Transaction{gas_price: Decimal.new("1e18")}, :ether) == Decimal.new(1)
test "without receipt with :ether unit" do
assert Chain.fee(%Transaction{gas: Decimal.new(3), gas_price: Decimal.new(2), receipt: nil}, :ether) ==
{:maximum, Decimal.new("6e-18")}
end
end
describe "from_address_to_transactions/2" do
test "without transactions" do
address = insert(:address)
assert Repo.aggregate(Transaction, :count, :id) == 0
assert %Scrivener.Page{
entries: [],
page_number: 1,
total_entries: 0
} = Chain.from_address_to_transactions(address)
test "with receipt with :wei unit" do
assert Chain.fee(
%Transaction{
gas: Decimal.new(3),
gas_price: Decimal.new(2),
receipt: %Receipt{gas_used: Decimal.new(2)}
},
:wei
) == {:actual, Decimal.new(4)}
end
test "with transactions" do
%Transaction{from_address_id: from_address_id, id: transaction_id} = insert(:transaction)
address = Repo.get!(Address, from_address_id)
assert %Scrivener.Page{
entries: [%Transaction{id: ^transaction_id}],
page_number: 1,
total_entries: 1
} = Chain.from_address_to_transactions(address)
test "with receipt with :gwei unit" do
assert Chain.fee(
%Transaction{
gas: Decimal.new(3),
gas_price: Decimal.new(2),
receipt: %Receipt{gas_used: Decimal.new(2)}
},
:gwei
) == {:actual, Decimal.new("4e-9")}
end
test "with transactions with receipt required without receipt does not return transaction" do
address = %Address{id: from_address_id} = insert(:address)
%Transaction{id: transaction_id_with_receipt} = insert(:transaction, from_address_id: from_address_id)
insert(:receipt, transaction_id: transaction_id_with_receipt)
%Transaction{id: transaction_id_without_receipt} = insert(:transaction, from_address_id: from_address_id)
assert %Scrivener.Page{
entries: [%Transaction{id: ^transaction_id_with_receipt, receipt: %Receipt{}}],
page_number: 1,
total_entries: 1
} =
Chain.from_address_to_transactions(
address,
necessity_by_association: %{receipt: :required}
)
assert %Scrivener.Page{
entries: transactions,
page_number: 1,
total_entries: 2
} =
Chain.from_address_to_transactions(
address,
necessity_by_association: %{receipt: :optional}
)
assert length(transactions) == 2
transaction_by_id =
Enum.into(transactions, %{}, fn transaction = %Transaction{id: id} ->
{id, transaction}
end)
test "with receipt with :ether unit" do
assert Chain.fee(
%Transaction{
gas: Decimal.new(3),
gas_price: Decimal.new(2),
receipt: %Receipt{gas_used: Decimal.new(2)}
},
:ether
) == {:actual, Decimal.new("4e-18")}
end
end
assert %Transaction{receipt: %Receipt{}} = transaction_by_id[transaction_id_with_receipt]
assert %Transaction{receipt: nil} = transaction_by_id[transaction_id_without_receipt]
describe "gas_price/2" do
test ":wei unit" do
assert Chain.gas_price(%Transaction{gas_price: Decimal.new(1)}, :wei) == Decimal.new(1)
end
test "with transactions can be paginated" do
adddress = %Address{id: from_address_id} = insert(:address)
transactions = insert_list(2, :transaction, from_address_id: from_address_id)
test ":gwei unit" do
assert Chain.gas_price(%Transaction{gas_price: Decimal.new(1)}, :gwei) == Decimal.new("1e-9")
[%Transaction{id: oldest_transaction_id}, %Transaction{id: newest_transaction_id}] = transactions
assert Chain.gas_price(%Transaction{gas_price: Decimal.new("1e9")}, :gwei) == Decimal.new(1)
end
assert %Scrivener.Page{
entries: [%Transaction{id: ^newest_transaction_id}],
page_number: 1,
page_size: 1,
total_entries: 2,
total_pages: 2
} = Chain.from_address_to_transactions(adddress, pagination: %{page_size: 1})
test ":ether unit" do
assert Chain.gas_price(%Transaction{gas_price: Decimal.new(1)}, :ether) == Decimal.new("1e-18")
assert %Scrivener.Page{
entries: [%Transaction{id: ^oldest_transaction_id}],
page_number: 2,
page_size: 1,
total_entries: 2,
total_pages: 2
} = Chain.from_address_to_transactions(adddress, pagination: %{page: 2, page_size: 1})
assert Chain.gas_price(%Transaction{gas_price: Decimal.new("1e18")}, :ether) == Decimal.new(1)
end
end
@ -442,94 +564,6 @@ defmodule Explorer.ChainTest do
end
end
describe "to_address_to_transactions/2" do
test "without transactions" do
address = insert(:address)
assert Repo.aggregate(Transaction, :count, :id) == 0
assert %Scrivener.Page{
entries: [],
page_number: 1,
total_entries: 0
} = Chain.to_address_to_transactions(address)
end
test "with transactions" do
%Transaction{to_address_id: to_address_id, id: transaction_id} = insert(:transaction)
address = Repo.get!(Address, to_address_id)
assert %Scrivener.Page{
entries: [%Transaction{id: ^transaction_id}],
page_number: 1,
total_entries: 1
} = Chain.to_address_to_transactions(address)
end
test "with transactions with receipt required without receipt does not return transaction" do
address = %Address{id: to_address_id} = insert(:address)
%Transaction{id: transaction_id_with_receipt} = insert(:transaction, to_address_id: to_address_id)
insert(:receipt, transaction_id: transaction_id_with_receipt)
%Transaction{id: transaction_id_without_receipt} = insert(:transaction, to_address_id: to_address_id)
assert %Scrivener.Page{
entries: [%Transaction{id: ^transaction_id_with_receipt, receipt: %Receipt{}}],
page_number: 1,
total_entries: 1
} =
Chain.to_address_to_transactions(
address,
necessity_by_association: %{receipt: :required}
)
assert %Scrivener.Page{
entries: transactions,
page_number: 1,
total_entries: 2
} =
Chain.to_address_to_transactions(
address,
necessity_by_association: %{receipt: :optional}
)
assert length(transactions) == 2
transaction_by_id =
Enum.into(transactions, %{}, fn transaction = %Transaction{id: id} ->
{id, transaction}
end)
assert %Transaction{receipt: %Receipt{}} = transaction_by_id[transaction_id_with_receipt]
assert %Transaction{receipt: nil} = transaction_by_id[transaction_id_without_receipt]
end
test "with transactions can be paginated" do
adddress = %Address{id: to_address_id} = insert(:address)
transactions = insert_list(2, :transaction, to_address_id: to_address_id)
[%Transaction{id: oldest_transaction_id}, %Transaction{id: newest_transaction_id}] = transactions
assert %Scrivener.Page{
entries: [%Transaction{id: ^newest_transaction_id}],
page_number: 1,
page_size: 1,
total_entries: 2,
total_pages: 2
} = Chain.to_address_to_transactions(adddress, pagination: %{page_size: 1})
assert %Scrivener.Page{
entries: [%Transaction{id: ^oldest_transaction_id}],
page_number: 2,
page_size: 1,
total_entries: 2,
total_pages: 2
} = Chain.to_address_to_transactions(adddress, pagination: %{page: 2, page_size: 1})
end
end
describe "transaction_count/0" do
test "without transactions" do
assert Chain.transaction_count() == 0

@ -1,17 +1,13 @@
body {
color: $ma-grey;
font-family: $ma-body-sans-serif;
color: $gray-500;
font-family: $font-family-sans-serif;
}
h1 {
font-size: 28px;
font-family: $ma-sans-serif;
@media screen and (max-width: 768px) {
font-size: 22px;
}
font-family: $font-family-sans-serif;
}
h2 {
@ -28,7 +24,7 @@ h4 {
font-size: 16px !important;
line-height: 24px !important;
font-weight: 300 !important;
color: $ma-md-grey !important;
color: $gray-600 !important;
margin-top: 4px !important;
&.underline {
@ -38,22 +34,16 @@ h4 {
p {
font-size: 12px;
color: $ma-md-grey;
color: $gray-600;
@media screen and (max-width: 768px) {
font-size: 14px !important;
}
}
.special {
font-size: 11px;
font-style: italic;
}
a {
text-decoration: none;
color: $ma-teal;
color: $gray-800;
font-size: 12px;
&:hover,
@ -61,7 +51,3 @@ a {
text-decoration: none;
}
}
.support {
font-size: 15px !important;
}

@ -461,25 +461,25 @@ $padding-xs: 5px;
// Border --------------------------------------------------
.u-border {
border: 1px solid $ma-darkblue;
border: 1px solid $primary;
&-left {
border-left: 1px solid lighten($gray-light, 42%);
border-left: 1px solid lighten($gray-300, 42%);
}
&-right {
border-right: 1px solid lighten($gray-light, 42%);
border-right: 1px solid lighten($gray-300, 42%);
}
&-top {
border-top: 1px solid lighten($gray-light, 42%);
border-top: 1px solid lighten($gray-300, 42%);
}
&-bottom {
border-bottom: 1px solid lighten($gray-light, 42%);
border-bottom: 1px solid lighten($gray-300, 42%);
&-bold {
border-bottom: 2px solid lighten($gray-light, 32%);
border-bottom: 2px solid lighten($gray-300, 32%);
}
}
@ -531,10 +531,6 @@ $padding-xs: 5px;
.u-underline {
text-decoration: underline;
&-active {
text-decoration-color:$ma-teal;
}
}
.u-list-style {
@ -575,7 +571,7 @@ $padding-xs: 5px;
right: 0;
height: 50%;
width: 100%;
background: linear-gradient(to bottom, rgba($ma-white, 0), rgba($ma-white, 1) 100%);
background: linear-gradient(to bottom, rgba($white, 0), rgba($white, 1) 100%);
}
}

@ -1,111 +0,0 @@
// Variables
// --------------------------------------------------
//== Colors
//
$ma-green: #80bb41;
$ma-grey: #414144;
$ma-md-grey: #797a7c;
$ma-lightgrey: #dddede;
$ma-teal: #009579;
$ma-blue: #2474bb;
$ma-darkblue: darken(#2474bb, 20%);
$ma-red: #c03b44;
$ma-white: #fff;
$ma-black: #2d2926;
$ma-evergreen: #16433c;
// Fonts
//
$ma-sans-serif: 'Boxed Bold', Helvetica, Arial, sans-serif;
$ma-body-sans-serif: 'Roboto', Helvetica, sans-serif;
// Bootstrap Variables:
$bootstrap-sass-asset-helper: false;
//## Gray and brand colors for use across Bootstrap.
$gray-base: #000 !default;
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
$gray-dark: lighten($gray-base, 20%) !default;
// #333
$gray: lighten($gray-base, 33.5%) !default; // #555
$gray-light: lighten($gray-base, 46.7%) !default; // #777
$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee
$brand-primary: $ma-blue;
$brand-success: $ma-green;
$brand-info: #5bc0de !default;
$brand-warning: #f0ad4e !default;
$brand-danger: $ma-red;
//== Scaffolding
//
//## Settings for some of the most global styles.
//** Background color for `<body>`.
$body-bg: #fff !default; //** Global text color on `<body>`.
$text-color: $gray-dark !default;
$text-inactive: $ma-lightgrey;
//** Global textual link color.
$link-color: $ma-darkblue!default; //** Link hover color set via `darken()` function.
$link-hover-color: darken($link-color, 15%) !default; //** Link hover decoration.
$link-hover-decoration: underline !default;
//== Typography
//
//## Font, line-height, and color for body text, headings, and more.
$font-family-sans-serif: "Boxed Bold", Helvetica, Arial, sans-serif !default;
$font-family-body-san-serif: "Roboto", Helvetica, Arial, sans-serif !default; //** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default;
$font-family-base: $font-family-sans-serif !default;
//** Unit-less `line-height` for use in components like buttons.
$line-height-base: 1.428571429 !default; // 20/14
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
$line-height-computed: floor(($font-size-base * $line-height-base)) !default; // ~20px
//** By default, this inherits from the `<body>`.
$headings-font-family: inherit !default;
$headings-font-weight: 500 !default;
$headings-line-height: 1.1 !default;
$headings-color: inherit !default;
//== Media queries breakpoints
//
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
// Extra small screen / phone
//** Deprecated `$screen-xs` as of v3.0.1
$screen-xs: 480px !default; //** Deprecated `$screen-xs-min` as of v3.2.0
$screen-xs-min: $screen-xs !default; //** Deprecated `$screen-phone` as of v3.0.1
$screen-phone: $screen-xs-min !default;
// Small screen / tablet
//** Deprecated `$screen-sm` as of v3.0.1
$screen-sm: 768px !default;
$screen-sm-min: $screen-sm !default; //** Deprecated `$screen-tablet` as of v3.0.1
$screen-tablet: $screen-sm-min !default;
// Medium screen / desktop
//** Deprecated `$screen-md` as of v3.0.1
$screen-md: 992px !default;
$screen-md-min: $screen-md !default; //** Deprecated `$screen-desktop` as of v3.0.1
$screen-desktop: $screen-md-min !default;
// Large screen / wide desktop
//** Deprecated `$screen-lg` as of v3.0.1
$screen-lg: 1200px !default;
$screen-lg-min: $screen-lg !default; //** Deprecated `$screen-lg-desktop` as of v3.0.1
$screen-lg-desktop: $screen-lg-min !default;
// So media queries don't overlap when required, provide a maximum
$screen-xs-max: ($screen-sm-min - 1) !default;
$screen-sm-max: ($screen-md-min - 1) !default;
$screen-md-max: ($screen-lg-min - 1) !default;
//== Grid system
//
//## Define your custom responsive grid.
//** Number of columns in the grid.
$grid-columns: 12 !default; //** Padding between columns. Gets divided in half for the left and right.
$grid-gutter-width: 30px !default; // Navbar collapse
//** Point at which the navbar becomes uncollapsed.
$grid-float-breakpoint: $screen-sm-min !default; //** Point at which the navbar begins collapsing.
$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
//== Container sizes
//
//## Define the maximum width of `.container` for different screen sizes.
// Small screen / tablet
$container-tablet: (720px + $grid-gutter-width) !default; //** For `$screen-sm-min` and up.
$container-sm: $container-tablet !default;
// Medium screen / desktop
$container-desktop: (940px + $grid-gutter-width) !default; //** For `$screen-md-min` and up.
$container-md: $container-desktop !default;
// Large screen / wide desktop
$container-large-desktop: (1140px + $grid-gutter-width) !default; //** For `$screen-lg-min` and up.
$container-lg: $container-large-desktop !default;

@ -5,11 +5,22 @@
.alert:empty { display: none; }
/* This file is for your main application css. */
$icon-font-path: "/fonts/"; /* use fonts from priv/static/fonts/ */
@import "bootstrap";
@import "explorer/sb-admin-2";
@import "variables";
@import "fonts";
@import "typography";
//Bootstrap Core CSS
@import "node_modules/bootstrap/scss/functions";
@import "theme/variables";
@import "node_modules/bootstrap/scss/mixins";
@import "node_modules/bootstrap/scss/root";
@import "node_modules/bootstrap/scss/reboot";
// Bootstrap Components
@import "node_modules/bootstrap/scss/dropdown";
@import "node_modules/bootstrap/scss/transitions";
//Custom SCSS
@import "theme/fonts";
@import "utilities";
@import "typography";
@import "explorer/button";
@import "explorer/filter";

@ -26,7 +26,7 @@
&__section {
display: block;
padding: 0 explorer-size(-2);
margin: 0 auto;
margin: 0 80px;
}
}

@ -8,84 +8,52 @@
border-radius: .14589803rem;
&--primary {
background-color: $ma-white !important;
border: 1px solid $ma-green;
color: $ma-black !important;
background-color: $primary !important;
color: $black !important;
-webkit-transition: background-color .25s, color .25s !important;
transition: background-color .25s, color .25s;
&:hover,
&:focus {
background-color: $ma-grey !important;
color: lighten($ma-grey, 60%) !important;
background-color: $gray-700 !important;
color: $gray-200 !important;
text-decoration: none;
}
&:disabled {
background-color: $ma-lightgrey;
color: lighten($ma-grey,40%);
background-color: $gray-300;
color: $gray-800;
text-decoration: none;
}
}
&--secondary {
background-color: explorer-color("gray", "400");
color: $ma-white;
background-color: $gray-300;
color: $gray-800;
font-size: 13px;
-webkit-transition: background-color .25s;
transition: background-color .25s;
font-weight: 300;
font-weight: 400;
&:hover,
&:focus {
background-color: explorer-color("slate", "900");
color: $ma-white;
background-color: explorer-color("gray", "400");
color: explorer-color("gray", "900");;
text-decoration: none;
outline: none !important;
}
}
&--tertiary {
background-color: lighten($ma-grey, 60%);
color: $ma-grey;
background-color: $gray-500;
color: $gray-800;
-webkit-transition: background-color .25s, color .25s;
transition: background-color .25s, color .25s;
&:hover,
&:focus {
background-color: $ma-grey;
color: lighten($ma-grey, 60%);
}
}
&--hidden {
color: darken($ma-green, 20%);
width: 112px;
font-weight: 400;
border: 1px solid lighten($ma-grey, 50%);
padding: 22px !important;
&:hover,
&:focus {
background-color: $ma-grey;
color: $ma-white;
text-decoration: none;
}
@media screen and (max-width: 768px) {
width: 100%;
}
}
&--activate {
border: 1px solid $ma-teal;
width: 100px;
color: $ma-teal;
-webkit-transition: background-color .25s, color .25s;
transition: background-color .25s, color .25s;
&:hover,
&:focus {
background-color: $gray-800;
color: $gray-400;
}
}
@ -119,8 +87,8 @@
}
&--disabled {
background-color: $ma-lightgrey;
color: lighten($ma-grey,40%);
background-color: $gray-300;
color: $gray-500;
text-decoration: none;
}

@ -0,0 +1,4 @@
.filter {
min-width: 100%;
transform: translate3d(1px, 26px, 0px) !important;
}

@ -1,430 +0,0 @@
body {
background-color: #f8f8f8;
}
#wrapper {
width: 100%;
}
#page-wrapper {
padding: 0 15px;
min-height: 568px;
background-color: white;
}
@media (min-width: 768px) {
#page-wrapper {
position: inherit;
margin: 0 0 0 250px;
padding: 0 30px;
border-left: 1px solid #e7e7e7;
}
}
.navbar-top-links {
margin-right: 0;
}
.navbar-top-links li {
display: inline-block;
}
.navbar-top-links li:last-child {
margin-right: 15px;
}
.navbar-top-links li a {
padding: 15px;
min-height: 50px;
}
.navbar-top-links .dropdown-menu li {
display: block;
}
.navbar-top-links .dropdown-menu li:last-child {
margin-right: 0;
}
.navbar-top-links .dropdown-menu li a {
padding: 3px 20px;
min-height: 0;
}
.navbar-top-links .dropdown-menu li a div {
white-space: normal;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
width: 310px;
min-width: 0;
}
.navbar-top-links .dropdown-messages {
margin-left: 5px;
}
.navbar-top-links .dropdown-tasks {
margin-left: -59px;
}
.navbar-top-links .dropdown-alerts {
margin-left: -123px;
}
.navbar-top-links .dropdown-user {
right: 0;
left: auto;
}
.sidebar .sidebar-nav.navbar-collapse {
padding-left: 0;
padding-right: 0;
}
.sidebar .sidebar-search {
padding: 15px;
}
.sidebar ul li {
border-bottom: 1px solid #e7e7e7;
}
.sidebar ul li a.active {
background-color: #eeeeee;
}
.sidebar .arrow {
float: right;
}
.sidebar .fa.arrow:before {
content: "\f104";
}
.sidebar .active > a > .fa.arrow:before {
content: "\f107";
}
.sidebar .nav-second-level li,
.sidebar .nav-third-level li {
border-bottom: none !important;
}
.sidebar .nav-second-level li a {
padding-left: 37px;
}
.sidebar .nav-third-level li a {
padding-left: 52px;
}
@media (min-width: 768px) {
.sidebar {
z-index: 1;
position: absolute;
width: 250px;
margin-top: 51px;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
margin-left: auto;
}
}
.btn-outline {
color: inherit;
background-color: transparent;
transition: all .5s;
}
.btn-primary.btn-outline {
color: #428bca;
}
.btn-success.btn-outline {
color: #5cb85c;
}
.btn-info.btn-outline {
color: #5bc0de;
}
.btn-warning.btn-outline {
color: #f0ad4e;
}
.btn-danger.btn-outline {
color: #d9534f;
}
.btn-primary.btn-outline:hover,
.btn-success.btn-outline:hover,
.btn-info.btn-outline:hover,
.btn-warning.btn-outline:hover,
.btn-danger.btn-outline:hover {
color: white;
}
.chat {
margin: 0;
padding: 0;
list-style: none;
}
.chat li {
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px dotted #999999;
}
.chat li.left .chat-body {
margin-left: 60px;
}
.chat li.right .chat-body {
margin-right: 60px;
}
.chat li .chat-body p {
margin: 0;
}
.panel .slidedown .glyphicon,
.chat .glyphicon {
margin-right: 5px;
}
.chat-panel .panel-body {
height: 350px;
overflow-y: scroll;
}
.login-panel {
margin-top: 25%;
}
.flot-chart {
display: block;
height: 400px;
}
.flot-chart-content {
width: 100%;
height: 100%;
}
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
background: transparent;
}
table.dataTable thead .sorting_asc:after {
content: "\f0de";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting_desc:after {
content: "\f0dd";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting:after {
content: "\f0dc";
float: right;
font-family: fontawesome;
color: rgba(50, 50, 50, 0.5);
}
.btn-circle {
width: 30px;
height: 30px;
padding: 6px 0;
border-radius: 15px;
text-align: center;
font-size: 12px;
line-height: 1.428571429;
}
.btn-circle.btn-lg {
width: 50px;
height: 50px;
padding: 10px 16px;
border-radius: 25px;
font-size: 18px;
line-height: 1.33;
}
.btn-circle.btn-xl {
width: 70px;
height: 70px;
padding: 10px 16px;
border-radius: 35px;
font-size: 24px;
line-height: 1.33;
}
.show-grid [class^="col-"] {
padding-top: 10px;
padding-bottom: 10px;
border: 1px solid #ddd;
background-color: #eee !important;
}
.show-grid {
margin: 15px 0;
}
.huge {
font-size: 40px;
}
.panel-green {
border-color: #5cb85c;
}
.panel-green > .panel-heading {
border-color: #5cb85c;
color: white;
background-color: #5cb85c;
}
.panel-green > a {
color: #5cb85c;
}
.panel-green > a:hover {
color: #3d8b3d;
}
.panel-red {
border-color: #d9534f;
}
.panel-red > .panel-heading {
border-color: #d9534f;
color: white;
background-color: #d9534f;
}
.panel-red > a {
color: #d9534f;
}
.panel-red > a:hover {
color: #b52b27;
}
.panel-yellow {
border-color: #f0ad4e;
}
.panel-yellow > .panel-heading {
border-color: #f0ad4e;
color: white;
background-color: #f0ad4e;
}
.panel-yellow > a {
color: #f0ad4e;
}
.panel-yellow > a:hover {
color: #df8a13;
}
.timeline {
position: relative;
padding: 20px 0 20px;
list-style: none;
}
.timeline:before {
content: " ";
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 3px;
margin-left: -1.5px;
background-color: #eeeeee;
}
.timeline > li {
position: relative;
margin-bottom: 20px;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li > .timeline-panel {
float: left;
position: relative;
width: 46%;
padding: 20px;
border: 1px solid #d4d4d4;
border-radius: 2px;
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
}
.timeline > li > .timeline-panel:before {
content: " ";
display: inline-block;
position: absolute;
top: 26px;
right: -15px;
border-top: 15px solid transparent;
border-right: 0 solid #ccc;
border-bottom: 15px solid transparent;
border-left: 15px solid #ccc;
}
.timeline > li > .timeline-panel:after {
content: " ";
display: inline-block;
position: absolute;
top: 27px;
right: -14px;
border-top: 14px solid transparent;
border-right: 0 solid #fff;
border-bottom: 14px solid transparent;
border-left: 14px solid #fff;
}
.timeline > li > .timeline-badge {
z-index: 100;
position: absolute;
top: 16px;
left: 50%;
width: 50px;
height: 50px;
margin-left: -25px;
border-radius: 50% 50% 50% 50%;
text-align: center;
font-size: 1.4em;
line-height: 50px;
color: #fff;
background-color: #999999;
}
.timeline > li.timeline-inverted > .timeline-panel {
float: right;
}
.timeline > li.timeline-inverted > .timeline-panel:before {
right: auto;
left: -15px;
border-right-width: 15px;
border-left-width: 0;
}
.timeline > li.timeline-inverted > .timeline-panel:after {
right: auto;
left: -14px;
border-right-width: 14px;
border-left-width: 0;
}
.timeline-badge.primary {
background-color: #2e6da4 !important;
}
.timeline-badge.success {
background-color: #3f903f !important;
}
.timeline-badge.warning {
background-color: #f0ad4e !important;
}
.timeline-badge.danger {
background-color: #d9534f !important;
}
.timeline-badge.info {
background-color: #5bc0de !important;
}
.timeline-title {
margin-top: 0;
color: inherit;
}
.timeline-body > p,
.timeline-body > ul {
margin-bottom: 0;
}
.timeline-body > p + p {
margin-top: 5px;
}
@media (max-width: 767px) {
ul.timeline:before {
left: 40px;
}
ul.timeline > li > .timeline-panel {
width: calc(10%);
width: -moz-calc(10%);
width: -webkit-calc(10%);
}
ul.timeline > li > .timeline-badge {
top: 16px;
left: 15px;
margin-left: 0;
}
ul.timeline > li > .timeline-panel {
float: right;
}
ul.timeline > li > .timeline-panel:before {
right: auto;
left: -15px;
border-right-width: 15px;
border-left-width: 0;
}
ul.timeline > li > .timeline-panel:after {
right: auto;
left: -14px;
border-right-width: 14px;
border-left-width: 0;
}
}

@ -31,9 +31,6 @@
font-weight: 700;
}
@font-face {
font-family: 'Boxed Bold';
src: url("/fonts/boxed-bold.otf");

@ -0,0 +1,928 @@
// Variables
//
// Variables should follow the `$component-state-property-size` formula for
// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
//
// Color system
//
// stylelint-disable
$white: #fff !default;
$gray-100: #f8f9fa !default;
$gray-200: #e9ecef !default;
$gray-300: #dee2e6 !default;
$gray-400: #ced4da !default;
$gray-500: #adb5bd !default;
$gray-600: #6c757d !default;
$gray-700: #495057 !default;
$gray-800: #343a40 !default;
$gray-900: #212529 !default;
$black: #000 !default;
$grays: () !default;
$grays: map-merge((
"100": $gray-100,
"200": $gray-200,
"300": $gray-300,
"400": $gray-400,
"500": $gray-500,
"600": $gray-600,
"700": $gray-700,
"800": $gray-800,
"900": $gray-900
), $grays);
$blue: #007bff !default;
$indigo: #6610f2 !default;
$purple: #6f42c1 !default;
$pink: #e83e8c !default;
$red: #dc3545 !default;
$orange: #fd7e14 !default;
$yellow: #ffc107 !default;
$green: #28a745 !default;
$teal: #20c997 !default;
$cyan: #17a2b8 !default;
$colors: () !default;
$colors: map-merge((
"blue": $blue,
"indigo": $indigo,
"purple": $purple,
"pink": $pink,
"red": $red,
"orange": $orange,
"yellow": $yellow,
"green": $green,
"teal": $teal,
"cyan": $cyan,
"white": $white,
"gray": $gray-600,
"gray-dark": $gray-800
), $colors);
$primary: $blue !default;
$secondary: $gray-600 !default;
$success: $green !default;
$info: $cyan !default;
$warning: $yellow !default;
$danger: $red !default;
$light: $gray-100 !default;
$dark: $gray-800 !default;
$theme-colors: () !default;
$theme-colors: map-merge((
"primary": $primary,
"secondary": $secondary,
"success": $success,
"info": $info,
"warning": $warning,
"danger": $danger,
"light": $light,
"dark": $dark
), $theme-colors);
// stylelint-enable
// Set a specific jump point for requesting color jumps
$theme-color-interval: 8% !default;
// The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255.
$yiq-contrasted-threshold: 150 !default;
// Customize the light and dark text colors for use in our YIQ color contrast function.
$yiq-text-dark: $gray-900 !default;
$yiq-text-light: $white !default;
// Options
//
// Quickly modify global styling by enabling or disabling optional features.
$enable-caret: true !default;
$enable-rounded: true !default;
$enable-shadows: false !default;
$enable-gradients: false !default;
$enable-transitions: true !default;
$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS
$enable-grid-classes: true !default;
$enable-print-styles: true !default;
// Spacing
//
// Control the default styling of most Bootstrap elements by modifying these
// variables. Mostly focused on spacing.
// You can add more entries to the $spacers map, should you need more variation.
// stylelint-disable
$spacer: 1rem !default;
$spacers: () !default;
$spacers: map-merge((
0: 0,
1: ($spacer * .25),
2: ($spacer * .5),
3: $spacer,
4: ($spacer * 1.5),
5: ($spacer * 3)
), $spacers);
// This variable affects the `.h-*` and `.w-*` classes.
$sizes: () !default;
$sizes: map-merge((
25: 25%,
50: 50%,
75: 75%,
100: 100%,
auto: auto
), $sizes);
// stylelint-enable
// Body
//
// Settings for the `<body>` element.
$body-bg: $white !default;
$body-color: $gray-900 !default;
// Links
//
// Style anchor elements.
$link-color: theme-color("primary") !default;
$link-decoration: none !default;
$link-hover-color: darken($link-color, 15%) !default;
$link-hover-decoration: underline !default;
// Paragraphs
//
// Style p element.
$paragraph-margin-bottom: 1rem !default;
// Grid breakpoints
//
// Define the minimum dimensions at which your layout will change,
// adapting to different screen sizes, for use in media queries.
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
) !default;
@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
@include _assert-starts-at-zero($grid-breakpoints);
// Grid containers
//
// Define the maximum width of `.container` for different screen sizes.
$container-max-widths: (
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px
) !default;
@include _assert-ascending($container-max-widths, "$container-max-widths");
// Grid columns
//
// Set the number of columns and specify the width of the gutters.
$grid-columns: 12 !default;
$grid-gutter-width: 30px !default;
// Components
//
// Define common padding and border radius sizes and more.
$line-height-lg: 1.5 !default;
$line-height-sm: 1.5 !default;
$border-width: 1px !default;
$border-color: $gray-300 !default;
$border-radius: .25rem !default;
$border-radius-lg: .3rem !default;
$border-radius-sm: .2rem !default;
$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;
$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;
$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;
$component-active-color: $white !default;
$component-active-bg: theme-color("primary") !default;
$caret-width: .3em !default;
$transition-base: all .2s ease-in-out !default;
$transition-fade: opacity .15s linear !default;
$transition-collapse: height .35s ease !default;
// Fonts
//
// Font, line-height, and color for body text, headings, and more.
// stylelint-disable value-keyword-case
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
$font-family-base: $font-family-sans-serif !default;
// stylelint-enable value-keyword-case
$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`
$font-size-lg: ($font-size-base * 1.25) !default;
$font-size-sm: ($font-size-base * .875) !default;
$font-weight-light: 300 !default;
$font-weight-normal: 400 !default;
$font-weight-bold: 700 !default;
$font-weight-base: $font-weight-normal !default;
$line-height-base: 1.5 !default;
$h1-font-size: $font-size-base * 2.5 !default;
$h2-font-size: $font-size-base * 2 !default;
$h3-font-size: $font-size-base * 1.75 !default;
$h4-font-size: $font-size-base * 1.5 !default;
$h5-font-size: $font-size-base * 1.25 !default;
$h6-font-size: $font-size-base !default;
$headings-margin-bottom: ($spacer / 2) !default;
$headings-font-family: inherit !default;
$headings-font-weight: 500 !default;
$headings-line-height: 1.2 !default;
$headings-color: inherit !default;
$display1-size: 6rem !default;
$display2-size: 5.5rem !default;
$display3-size: 4.5rem !default;
$display4-size: 3.5rem !default;
$display1-weight: 300 !default;
$display2-weight: 300 !default;
$display3-weight: 300 !default;
$display4-weight: 300 !default;
$display-line-height: $headings-line-height !default;
$lead-font-size: ($font-size-base * 1.25) !default;
$lead-font-weight: 300 !default;
$small-font-size: 80% !default;
$text-muted: $gray-600 !default;
$blockquote-small-color: $gray-600 !default;
$blockquote-font-size: ($font-size-base * 1.25) !default;
$hr-border-color: rgba($black, .1) !default;
$hr-border-width: $border-width !default;
$mark-padding: .2em !default;
$dt-font-weight: $font-weight-bold !default;
$kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default;
$nested-kbd-font-weight: $font-weight-bold !default;
$list-inline-padding: .5rem !default;
$mark-bg: #fcf8e3 !default;
$hr-margin-y: $spacer !default;
// Tables
//
// Customizes the `.table` component with basic values, each used across all table variations.
$table-cell-padding: .75rem !default;
$table-cell-padding-sm: .3rem !default;
$table-bg: transparent !default;
$table-accent-bg: rgba($black, .05) !default;
$table-hover-bg: rgba($black, .075) !default;
$table-active-bg: $table-hover-bg !default;
$table-border-width: $border-width !default;
$table-border-color: $gray-300 !default;
$table-head-bg: $gray-200 !default;
$table-head-color: $gray-700 !default;
$table-dark-bg: $gray-900 !default;
$table-dark-accent-bg: rgba($white, .05) !default;
$table-dark-hover-bg: rgba($white, .075) !default;
$table-dark-border-color: lighten($gray-900, 7.5%) !default;
$table-dark-color: $body-bg !default;
$table-striped-order: odd !default;
$table-caption-color: $text-muted !default;
// Buttons + Forms
//
// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.
$input-btn-padding-y: .375rem !default;
$input-btn-padding-x: .75rem !default;
$input-btn-line-height: $line-height-base !default;
$input-btn-focus-width: .2rem !default;
$input-btn-focus-color: rgba($component-active-bg, .25) !default;
$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;
$input-btn-padding-y-sm: .25rem !default;
$input-btn-padding-x-sm: .5rem !default;
$input-btn-line-height-sm: $line-height-sm !default;
$input-btn-padding-y-lg: .5rem !default;
$input-btn-padding-x-lg: 1rem !default;
$input-btn-line-height-lg: $line-height-lg !default;
$input-btn-border-width: $border-width !default;
// Buttons
//
// For each of Bootstrap's buttons, define text, background, and border color.
$btn-padding-y: $input-btn-padding-y !default;
$btn-padding-x: $input-btn-padding-x !default;
$btn-line-height: $input-btn-line-height !default;
$btn-padding-y-sm: $input-btn-padding-y-sm !default;
$btn-padding-x-sm: $input-btn-padding-x-sm !default;
$btn-line-height-sm: $input-btn-line-height-sm !default;
$btn-padding-y-lg: $input-btn-padding-y-lg !default;
$btn-padding-x-lg: $input-btn-padding-x-lg !default;
$btn-line-height-lg: $input-btn-line-height-lg !default;
$btn-border-width: $input-btn-border-width !default;
$btn-font-weight: $font-weight-normal !default;
$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;
$btn-focus-width: $input-btn-focus-width !default;
$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;
$btn-disabled-opacity: .65 !default;
$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;
$btn-link-disabled-color: $gray-600 !default;
$btn-block-spacing-y: .5rem !default;
// Allows for customizing button radius independently from global border radius
$btn-border-radius: $border-radius !default;
$btn-border-radius-lg: $border-radius-lg !default;
$btn-border-radius-sm: $border-radius-sm !default;
$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
// Forms
$label-margin-bottom: .5rem !default;
$input-padding-y: $input-btn-padding-y !default;
$input-padding-x: $input-btn-padding-x !default;
$input-line-height: $input-btn-line-height !default;
$input-padding-y-sm: $input-btn-padding-y-sm !default;
$input-padding-x-sm: $input-btn-padding-x-sm !default;
$input-line-height-sm: $input-btn-line-height-sm !default;
$input-padding-y-lg: $input-btn-padding-y-lg !default;
$input-padding-x-lg: $input-btn-padding-x-lg !default;
$input-line-height-lg: $input-btn-line-height-lg !default;
$input-bg: $white !default;
$input-disabled-bg: $gray-200 !default;
$input-color: $gray-700 !default;
$input-border-color: $gray-400 !default;
$input-border-width: $input-btn-border-width !default;
$input-box-shadow: inset 0 1px 1px rgba($black, .075) !default;
$input-border-radius: $border-radius !default;
$input-border-radius-lg: $border-radius-lg !default;
$input-border-radius-sm: $border-radius-sm !default;
$input-focus-bg: $input-bg !default;
$input-focus-border-color: lighten($component-active-bg, 25%) !default;
$input-focus-color: $input-color !default;
$input-focus-width: $input-btn-focus-width !default;
$input-focus-box-shadow: $input-btn-focus-box-shadow !default;
$input-placeholder-color: $gray-600 !default;
$input-plaintext-color: $body-color !default;
$input-height-border: $input-border-width * 2 !default;
$input-height-inner: ($font-size-base * $input-btn-line-height) + ($input-btn-padding-y * 2) !default;
$input-height: calc(#{$input-height-inner} + #{$input-height-border}) !default;
$input-height-inner-sm: ($font-size-sm * $input-btn-line-height-sm) + ($input-btn-padding-y-sm * 2) !default;
$input-height-sm: calc(#{$input-height-inner-sm} + #{$input-height-border}) !default;
$input-height-inner-lg: ($font-size-lg * $input-btn-line-height-lg) + ($input-btn-padding-y-lg * 2) !default;
$input-height-lg: calc(#{$input-height-inner-lg} + #{$input-height-border}) !default;
$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
$form-text-margin-top: .25rem !default;
$form-check-input-gutter: 1.25rem !default;
$form-check-input-margin-y: .3rem !default;
$form-check-input-margin-x: .25rem !default;
$form-check-inline-margin-x: .75rem !default;
$form-check-inline-input-margin-x: .3125rem !default;
$form-group-margin-bottom: 1rem !default;
$input-group-addon-color: $input-color !default;
$input-group-addon-bg: $gray-200 !default;
$input-group-addon-border-color: $input-border-color !default;
$custom-control-gutter: 1.5rem !default;
$custom-control-spacer-x: 1rem !default;
$custom-control-indicator-size: 1rem !default;
$custom-control-indicator-bg: $gray-300 !default;
$custom-control-indicator-bg-size: 50% 50% !default;
$custom-control-indicator-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;
$custom-control-indicator-disabled-bg: $gray-200 !default;
$custom-control-label-disabled-color: $gray-600 !default;
$custom-control-indicator-checked-color: $component-active-color !default;
$custom-control-indicator-checked-bg: $component-active-bg !default;
$custom-control-indicator-checked-disabled-bg: rgba(theme-color("primary"), .5) !default;
$custom-control-indicator-checked-box-shadow: none !default;
$custom-control-indicator-focus-box-shadow: 0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;
$custom-control-indicator-active-color: $component-active-color !default;
$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default;
$custom-control-indicator-active-box-shadow: none !default;
$custom-checkbox-indicator-border-radius: $border-radius !default;
$custom-checkbox-indicator-icon-checked: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default;
$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default;
$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;
$custom-checkbox-indicator-icon-indeterminate: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23") !default;
$custom-checkbox-indicator-indeterminate-box-shadow: none !default;
$custom-radio-indicator-border-radius: 50% !default;
$custom-radio-indicator-icon-checked: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E"), "#", "%23") !default;
$custom-select-padding-y: .375rem !default;
$custom-select-padding-x: .75rem !default;
$custom-select-height: $input-height !default;
$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator
$custom-select-line-height: $input-btn-line-height !default;
$custom-select-color: $input-color !default;
$custom-select-disabled-color: $gray-600 !default;
$custom-select-bg: $input-bg !default;
$custom-select-disabled-bg: $gray-200 !default;
$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions
$custom-select-indicator-color: $gray-800 !default;
$custom-select-indicator: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23") !default;
$custom-select-border-width: $input-btn-border-width !default;
$custom-select-border-color: $input-border-color !default;
$custom-select-border-radius: $border-radius !default;
$custom-select-focus-border-color: $input-focus-border-color !default;
$custom-select-focus-box-shadow: inset 0 1px 2px rgba($black, .075), 0 0 5px rgba($custom-select-focus-border-color, .5) !default;
$custom-select-font-size-sm: 75% !default;
$custom-select-height-sm: $input-height-sm !default;
$custom-select-font-size-lg: 125% !default;
$custom-select-height-lg: $input-height-lg !default;
$custom-range-track-width: 100% !default;
$custom-range-track-height: .5rem !default;
$custom-range-track-cursor: pointer !default;
$custom-range-track-bg: $gray-300 !default;
$custom-range-track-border-radius: 1rem !default;
$custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;
$custom-range-thumb-width: 1rem !default;
$custom-range-thumb-height: $custom-range-thumb-width !default;
$custom-range-thumb-bg: $component-active-bg !default;
$custom-range-thumb-border: 0 !default;
$custom-range-thumb-border-radius: 1rem !default;
$custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;
$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;
$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;
$custom-file-height: $input-height !default;
$custom-file-focus-border-color: $input-focus-border-color !default;
$custom-file-focus-box-shadow: $input-btn-focus-box-shadow !default;
$custom-file-padding-y: $input-btn-padding-y !default;
$custom-file-padding-x: $input-btn-padding-x !default;
$custom-file-line-height: $input-btn-line-height !default;
$custom-file-color: $input-color !default;
$custom-file-bg: $input-bg !default;
$custom-file-border-width: $input-btn-border-width !default;
$custom-file-border-color: $input-border-color !default;
$custom-file-border-radius: $input-border-radius !default;
$custom-file-box-shadow: $input-box-shadow !default;
$custom-file-button-color: $custom-file-color !default;
$custom-file-button-bg: $input-group-addon-bg !default;
$custom-file-text: (
en: "Browse"
) !default;
// Form validation
$form-feedback-margin-top: $form-text-margin-top !default;
$form-feedback-font-size: $small-font-size !default;
$form-feedback-valid-color: theme-color("success") !default;
$form-feedback-invalid-color: theme-color("danger") !default;
// Dropdowns
//
// Dropdown menu container and contents.
$dropdown-min-width: 10rem !default;
$dropdown-padding-y: .5rem !default;
$dropdown-spacer: .125rem !default;
$dropdown-bg: $white !default;
$dropdown-border-color: rgba($black, .15) !default;
$dropdown-border-radius: $border-radius !default;
$dropdown-border-width: $border-width !default;
$dropdown-divider-bg: $gray-200 !default;
$dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;
$dropdown-link-color: $gray-900 !default;
$dropdown-link-hover-color: darken($gray-900, 5%) !default;
$dropdown-link-hover-bg: $gray-100 !default;
$dropdown-link-active-color: $component-active-color !default;
$dropdown-link-active-bg: $component-active-bg !default;
$dropdown-link-disabled-color: $gray-600 !default;
$dropdown-item-padding-y: .25rem !default;
$dropdown-item-padding-x: 1.5rem !default;
$dropdown-header-color: $gray-600 !default;
// Z-index master list
//
// Warning: Avoid customizing these values. They're used for a bird's eye view
// of components dependent on the z-axis and are designed to all work together.
$zindex-dropdown: 1000 !default;
$zindex-sticky: 1020 !default;
$zindex-fixed: 1030 !default;
$zindex-modal-backdrop: 1040 !default;
$zindex-modal: 1050 !default;
$zindex-popover: 1060 !default;
$zindex-tooltip: 1070 !default;
// Navs
$nav-link-padding-y: .5rem !default;
$nav-link-padding-x: 1rem !default;
$nav-link-disabled-color: $gray-600 !default;
$nav-tabs-border-color: $gray-300 !default;
$nav-tabs-border-width: $border-width !default;
$nav-tabs-border-radius: $border-radius !default;
$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;
$nav-tabs-link-active-color: $gray-700 !default;
$nav-tabs-link-active-bg: $body-bg !default;
$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;
$nav-pills-border-radius: $border-radius !default;
$nav-pills-link-active-color: $component-active-color !default;
$nav-pills-link-active-bg: $component-active-bg !default;
$nav-divider-color: $gray-200 !default;
$nav-divider-margin-y: ($spacer / 2) !default;
// Navbar
$navbar-padding-y: ($spacer / 2) !default;
$navbar-padding-x: $spacer !default;
$navbar-nav-link-padding-x: .5rem !default;
$navbar-brand-font-size: $font-size-lg !default;
// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
$nav-link-height: ($font-size-base * $line-height-base + $nav-link-padding-y * 2) !default;
$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;
$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;
$navbar-toggler-padding-y: .25rem !default;
$navbar-toggler-padding-x: .75rem !default;
$navbar-toggler-font-size: $font-size-lg !default;
$navbar-toggler-border-radius: $btn-border-radius !default;
$navbar-dark-color: rgba($white, .5) !default;
$navbar-dark-hover-color: rgba($white, .75) !default;
$navbar-dark-active-color: $white !default;
$navbar-dark-disabled-color: rgba($white, .25) !default;
$navbar-dark-toggler-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
$navbar-dark-toggler-border-color: rgba($white, .1) !default;
$navbar-light-color: rgba($black, .5) !default;
$navbar-light-hover-color: rgba($black, .7) !default;
$navbar-light-active-color: rgba($black, .9) !default;
$navbar-light-disabled-color: rgba($black, .3) !default;
$navbar-light-toggler-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
$navbar-light-toggler-border-color: rgba($black, .1) !default;
// Pagination
$pagination-padding-y: .5rem !default;
$pagination-padding-x: .75rem !default;
$pagination-padding-y-sm: .25rem !default;
$pagination-padding-x-sm: .5rem !default;
$pagination-padding-y-lg: .75rem !default;
$pagination-padding-x-lg: 1.5rem !default;
$pagination-line-height: 1.25 !default;
$pagination-color: $link-color !default;
$pagination-bg: $white !default;
$pagination-border-width: $border-width !default;
$pagination-border-color: $gray-300 !default;
$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;
$pagination-focus-outline: 0 !default;
$pagination-hover-color: $link-hover-color !default;
$pagination-hover-bg: $gray-200 !default;
$pagination-hover-border-color: $gray-300 !default;
$pagination-active-color: $component-active-color !default;
$pagination-active-bg: $component-active-bg !default;
$pagination-active-border-color: $pagination-active-bg !default;
$pagination-disabled-color: $gray-600 !default;
$pagination-disabled-bg: $white !default;
$pagination-disabled-border-color: $gray-300 !default;
// Jumbotron
$jumbotron-padding: 2rem !default;
$jumbotron-bg: $gray-200 !default;
// Cards
$card-spacer-y: .75rem !default;
$card-spacer-x: 1.25rem !default;
$card-border-width: $border-width !default;
$card-border-radius: $border-radius !default;
$card-border-color: rgba($black, .125) !default;
$card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}) !default;
$card-cap-bg: rgba($black, .03) !default;
$card-bg: $white !default;
$card-img-overlay-padding: 1.25rem !default;
$card-group-margin: ($grid-gutter-width / 2) !default;
$card-deck-margin: $card-group-margin !default;
$card-columns-count: 3 !default;
$card-columns-gap: 1.25rem !default;
$card-columns-margin: $card-spacer-y !default;
// Tooltips
$tooltip-font-size: $font-size-sm !default;
$tooltip-max-width: 200px !default;
$tooltip-color: $white !default;
$tooltip-bg: $black !default;
$tooltip-border-radius: $border-radius !default;
$tooltip-opacity: .9 !default;
$tooltip-padding-y: .25rem !default;
$tooltip-padding-x: .5rem !default;
$tooltip-margin: 0 !default;
$tooltip-arrow-width: .8rem !default;
$tooltip-arrow-height: .4rem !default;
$tooltip-arrow-color: $tooltip-bg !default;
// Popovers
$popover-font-size: $font-size-sm !default;
$popover-bg: $white !default;
$popover-max-width: 276px !default;
$popover-border-width: $border-width !default;
$popover-border-color: rgba($black, .2) !default;
$popover-border-radius: $border-radius-lg !default;
$popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default;
$popover-header-bg: darken($popover-bg, 3%) !default;
$popover-header-color: $headings-color !default;
$popover-header-padding-y: .5rem !default;
$popover-header-padding-x: .75rem !default;
$popover-body-color: $body-color !default;
$popover-body-padding-y: $popover-header-padding-y !default;
$popover-body-padding-x: $popover-header-padding-x !default;
$popover-arrow-width: 1rem !default;
$popover-arrow-height: .5rem !default;
$popover-arrow-color: $popover-bg !default;
$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;
// Badges
$badge-font-size: 75% !default;
$badge-font-weight: $font-weight-bold !default;
$badge-padding-y: .25em !default;
$badge-padding-x: .4em !default;
$badge-border-radius: $border-radius !default;
$badge-pill-padding-x: .6em !default;
// Use a higher than normal value to ensure completely rounded edges when
// customizing padding or font-size on labels.
$badge-pill-border-radius: 10rem !default;
// Modals
// Padding applied to the modal body
$modal-inner-padding: 1rem !default;
$modal-dialog-margin: .5rem !default;
$modal-dialog-margin-y-sm-up: 1.75rem !default;
$modal-title-line-height: $line-height-base !default;
$modal-content-bg: $white !default;
$modal-content-border-color: rgba($black, .2) !default;
$modal-content-border-width: $border-width !default;
$modal-content-border-radius: $border-radius-lg !default;
$modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default;
$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;
$modal-backdrop-bg: $black !default;
$modal-backdrop-opacity: .5 !default;
$modal-header-border-color: $gray-200 !default;
$modal-footer-border-color: $modal-header-border-color !default;
$modal-header-border-width: $modal-content-border-width !default;
$modal-footer-border-width: $modal-header-border-width !default;
$modal-header-padding: 1rem !default;
$modal-lg: 800px !default;
$modal-md: 500px !default;
$modal-sm: 300px !default;
$modal-transition: transform .3s ease-out !default;
// Alerts
//
// Define alert colors, border radius, and padding.
$alert-padding-y: .75rem !default;
$alert-padding-x: 1.25rem !default;
$alert-margin-bottom: 1rem !default;
$alert-border-radius: $border-radius !default;
$alert-link-font-weight: $font-weight-bold !default;
$alert-border-width: $border-width !default;
$alert-bg-level: -10 !default;
$alert-border-level: -9 !default;
$alert-color-level: 6 !default;
// Progress bars
$progress-height: 1rem !default;
$progress-font-size: ($font-size-base * .75) !default;
$progress-bg: $gray-200 !default;
$progress-border-radius: $border-radius !default;
$progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default;
$progress-bar-color: $white !default;
$progress-bar-bg: theme-color("primary") !default;
$progress-bar-animation-timing: 1s linear infinite !default;
$progress-bar-transition: width .6s ease !default;
// List group
$list-group-bg: $white !default;
$list-group-border-color: rgba($black, .125) !default;
$list-group-border-width: $border-width !default;
$list-group-border-radius: $border-radius !default;
$list-group-item-padding-y: .75rem !default;
$list-group-item-padding-x: 1.25rem !default;
$list-group-hover-bg: $gray-100 !default;
$list-group-active-color: $component-active-color !default;
$list-group-active-bg: $component-active-bg !default;
$list-group-active-border-color: $list-group-active-bg !default;
$list-group-disabled-color: $gray-600 !default;
$list-group-disabled-bg: $list-group-bg !default;
$list-group-action-color: $gray-700 !default;
$list-group-action-hover-color: $list-group-action-color !default;
$list-group-action-active-color: $body-color !default;
$list-group-action-active-bg: $gray-200 !default;
// Image thumbnails
$thumbnail-padding: .25rem !default;
$thumbnail-bg: $body-bg !default;
$thumbnail-border-width: $border-width !default;
$thumbnail-border-color: $gray-300 !default;
$thumbnail-border-radius: $border-radius !default;
$thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default;
// Figures
$figure-caption-font-size: 90% !default;
$figure-caption-color: $gray-600 !default;
// Breadcrumbs
$breadcrumb-padding-y: .75rem !default;
$breadcrumb-padding-x: 1rem !default;
$breadcrumb-item-padding: .5rem !default;
$breadcrumb-margin-bottom: 1rem !default;
$breadcrumb-bg: $gray-200 !default;
$breadcrumb-divider-color: $gray-600 !default;
$breadcrumb-active-color: $gray-600 !default;
$breadcrumb-divider: quote("/") !default;
$breadcrumb-border-radius: $border-radius !default;
// Carousel
$carousel-control-color: $white !default;
$carousel-control-width: 15% !default;
$carousel-control-opacity: .5 !default;
$carousel-indicator-width: 30px !default;
$carousel-indicator-height: 3px !default;
$carousel-indicator-spacer: 3px !default;
$carousel-indicator-active-bg: $white !default;
$carousel-caption-width: 70% !default;
$carousel-caption-color: $white !default;
$carousel-control-icon-width: 20px !default;
$carousel-control-prev-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23") !default;
$carousel-control-next-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23") !default;
$carousel-transition: transform .6s ease !default; // Define transform transition first if using multiple transitons (e.g., `transform 2s ease, opacity .5s ease-out`)
// Close
$close-font-size: $font-size-base * 1.5 !default;
$close-font-weight: $font-weight-bold !default;
$close-color: $black !default;
$close-text-shadow: 0 1px 0 $white !default;
// Code
$code-font-size: 87.5% !default;
$code-color: $pink !default;
$kbd-padding-y: .2rem !default;
$kbd-padding-x: .4rem !default;
$kbd-font-size: $code-font-size !default;
$kbd-color: $white !default;
$kbd-bg: $gray-900 !default;
$pre-color: $gray-900 !default;
$pre-scrollable-max-height: 340px !default;
// Printing
$print-page-size: a3 !default;
$print-body-min-width: map-get($grid-breakpoints, "lg") !default;

@ -1,14 +1,7 @@
defmodule ExplorerWeb.AddressController do
use ExplorerWeb, :controller
alias Explorer.Chain
def show(conn, %{"id" => hash}) do
hash
|> Chain.hash_to_address()
|> case do
{:ok, address} -> render(conn, "show.html", address: address)
{:error, :not_found} -> not_found(conn)
end
def show(conn, %{"id" => id, "locale" => locale}) do
redirect(conn, to: address_transaction_path(conn, :index, locale, id))
end
end

@ -0,0 +1,45 @@
defmodule ExplorerWeb.AddressTransactionController do
@moduledoc """
Display all the Transactions that terminate at this Address.
"""
use ExplorerWeb, :controller
alias Explorer.Chain
def index(conn, %{"address_id" => address_hash} = params) do
case Chain.hash_to_address(address_hash) do
{:ok, address} ->
options = [
necessity_by_association: %{
block: :required,
from_address: :optional,
to_address: :optional,
receipt: :required
},
pagination: params
]
page =
Chain.address_to_transactions(
address,
Keyword.merge(options, current_filter(params))
)
render(conn, "index.html", address: address, filter: params["filter"], page: page)
{:error, :not_found} ->
not_found(conn)
end
end
defp current_filter(params) do
params
|> Map.get("filter")
|> case do
"to" -> [direction: :to]
"from" -> [direction: :from]
_ -> []
end
end
end

@ -1,31 +0,0 @@
defmodule ExplorerWeb.AddressTransactionFromController do
@moduledoc """
Display all the Transactions that originate at this Address.
"""
use ExplorerWeb, :controller
alias Explorer.Chain
def index(conn, %{"address_id" => from_address_hash} = params) do
case Chain.hash_to_address(from_address_hash) do
{:ok, from_address} ->
page =
Chain.from_address_to_transactions(
from_address,
necessity_by_association: %{
block: :required,
from_address: :optional,
to_address: :optional,
receipt: :required
},
pagination: params
)
render(conn, "index.html", page: page)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -1,31 +0,0 @@
defmodule ExplorerWeb.AddressTransactionToController do
@moduledoc """
Display all the Transactions that terminate at this Address.
"""
use ExplorerWeb, :controller
alias Explorer.Chain
def index(conn, %{"address_id" => to_address_hash} = params) do
case Chain.hash_to_address(to_address_hash) do
{:ok, to_address} ->
page =
Chain.to_address_to_transactions(
to_address,
necessity_by_association: %{
block: :required,
from_address: :optional,
to_address: :optional,
receipt: :required
},
pagination: params
)
render(conn, "index.html", page: page)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -77,17 +77,10 @@ defmodule ExplorerWeb.Router do
resources "/addresses", AddressController, only: [:show] do
resources(
"/transactions_to",
AddressTransactionToController,
"/transactions",
AddressTransactionController,
only: [:index],
as: :transaction_to
)
resources(
"/transactions_from",
AddressTransactionFromController,
only: [:index],
as: :transaction_from
as: :transaction
)
end

@ -1,38 +0,0 @@
<section class="container__section address">
<div class="address__header">
<h1 class="address__heading"><%= gettext "Address" %></h1>
<h3 class="address__subheading"><%= @address.hash %></h3>
</div>
<div class="address__container">
<div class="address__tabs">
<h2 class="address__tab address__tab--active">
<%= link(
gettext("Overview"),
to: address_path(@conn, :show, @conn.assigns.locale, @address.hash),
class: "address__link address__link--active"
) %>
</h2>
<h2 class="address__tab">
<%= link(gettext("Transactions To"), to: address_transaction_to_path(@conn, :index, @conn.assigns.locale, @address.hash), class: "address__link") %>
</h2>
<h2 class="address__tab">
<%= link(
gettext("Transactions From"),
to: address_transaction_from_path(@conn, :index, @conn.assigns.locale, @address.hash),
class: "address__link"
) %>
</h2>
</div>
<div class="address__attributes">
<dl>
<div class="address__item">
<dt class="address__item-key"><%= gettext "Balance" %></dt>
<dd class="address__item-value address__balance" title="<%= @address.hash %>">
<%= format_balance(@address.balance) %> <%= gettext "Ether" %>
</dd>
</div>
</dl>
</div>
</div>
</section>

@ -1,43 +1,65 @@
<section class="container__section block">
<div class="address__headline">
<h1 class="address__headline-title"><%= gettext("Address %{number}", number: @conn.params["address_id"]) %></h1>
<div class="address__pagination">
<%= pagination_links(
@conn,
@page,
["en", @conn.params["address_id"]],
distance: 1,
first: true,
next: Phoenix.HTML.raw("&rsaquo;"),
path: &address_transaction_to_path/5,
previous: Phoenix.HTML.raw("&lsaquo;"),
view_style: :bulma
) %>
<div class="address__header">
<h1 class="address__heading"><%= gettext "Address" %></h1>
<h3 class="address__subheading"><%= @address.hash %></h3>
</div>
<div class="address__container">
<div class="address__attributes">
<dl>
<div class="address__item">
<dt class="address__item-key"><%= gettext "Balance" %></dt>
<dd class="address__item-value address__balance" title="<%= @address.hash %>">
<%= balance(@address) %> <%= gettext "Ether" %>
</dd>
</div>
</dl>
</div>
</div>
<div class="address__container">
<div class="address__tabs">
<h2 class="address__tab">
<%= link(
gettext("Overview"),
class: "address__link",
to: address_path(@conn, :show, @conn.assigns.locale, @conn.params["address_id"])
) %>
</h2>
<h2 class="address__tab address__tab--active">
<%= link(
gettext("Transactions To"),
gettext("Transactions"),
class: "address__link address__link--active",
to: address_transaction_to_path(@conn, :index, @conn.assigns.locale, @conn.params["address_id"])
to: address_transaction_path(@conn, :index, @conn.assigns.locale, @conn.params["address_id"])
) %>
</h2>
<h2 class="address__tab">
</div>
<div class="dropdown u-float-right u-push-sm-right u-push-sm-bottom">
<button data-test="filterDropdown" class="button button--secondary dropdown-toggle" type="button"
id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Filter: <%= format_current_filter(@filter) %>
</button>
<div class="dropdown-menu dropdown-menu-right filter" aria-labelledby="dropdownMenu2">
<%= link(
gettext("Transactions From"),
class: "address__link",
to: address_transaction_from_path(@conn, :index, @conn.assigns.locale, @conn.params["address_id"])
gettext("All"),
class: "address__link address__link--active dropdown-item",
to: address_transaction_path(@conn, :index, @conn.assigns.locale, @conn.params["address_id"])
) %>
</h2>
<%= link(
gettext("To"),
class: "address__link address__link--active dropdown-item",
to: address_transaction_path(
@conn,
:index,
@conn.assigns.locale,
@conn.params["address_id"],
filter: "to"
)
) %>
<%= link(
gettext("From"),
class: "address__link address__link--active dropdown-item",
to: address_transaction_path(
@conn,
:index,
@conn.assigns.locale,
@conn.params["address_id"],
filter: "from"
)
) %>
</div>
</div>
<div class="transactions__container">
<table class="transactions__table">
@ -52,6 +74,7 @@
<th class="transactions__column-header transactions__column-header--optional"><%= gettext "From" %></th>
<th class="transactions__column-header transactions__column-header--optional"><%= gettext "To" %></th>
<th class="transactions__column-header"><%= gettext "Value" %></th>
<th class="transactions__column-header"><%= gettext "Fee" %></th>
</tr>
</thead>
<tbody>
@ -100,10 +123,27 @@
<td class="transactions__column transactions__column--value">
<%= value(transaction) %> <%= gettext "Ether" %>
</td>
<td class="transactions__column transactions__column--value">
<%= fee(transaction) %> <%= gettext "Ether" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<div class="address__pagination">
<%= pagination_links(
@conn,
@page,
["en", @conn.params["address_id"]],
distance: 1,
filter: @conn.params["filter"],
first: true,
next: Phoenix.HTML.raw("&rsaquo;"),
path: &address_transaction_path/5,
previous: Phoenix.HTML.raw("&lsaquo;"),
view_style: :bulma
) %>
</div>
</section>

@ -1,108 +0,0 @@
<section class="container__section block">
<div class="address__headline">
<h1 class="address__headline-title"><%= gettext("Address %{number}", number: @conn.params["address_id"]) %></h1>
<div class="address__pagination"><%= pagination_links @conn, @page, ["en", @conn.params["address_id"]], view_style: :bulma, first: true, distance: 1, previous: Phoenix.HTML.raw("&lsaquo;"), next: Phoenix.HTML.raw("&rsaquo;"), path: &address_transaction_to_path/5 %></div>
<div class="address__pagination">
<%= pagination_links(
@conn,
@page,
["en", @conn.params["address_id"]],
distance: 1,
first: true,
next: Phoenix.HTML.raw("&rsaquo;"),
path: &address_transaction_to_path/5,
previous: Phoenix.HTML.raw("&lsaquo;"),
view_style: :bulma
) %>
</div>
</div>
<div class="address__container">
<div class="address__tabs">
<h2 class="address__tab">
<%= link(
gettext("Overview"),
class: "address__link",
to: address_path(@conn, :show, @conn.assigns.locale, @conn.params["address_id"])
) %>
</h2>
<h2 class="address__tab">
<%= link(
gettext("Transactions To"),
class: "address__link",
to: address_transaction_to_path(@conn, :index, @conn.assigns.locale, @conn.params["address_id"])
) %>
</h2>
<h2 class="address__tab address__tab--active">
<%= link(
gettext("Transactions From"),
class: "address__link address__link--active",
to: address_transaction_from_path(@conn, :index, @conn.assigns.locale, @conn.params["address_id"])
) %>
</h2>
</div>
<div class="transactions__container">
<table class="transactions__table">
<thead class="transactions__header">
<tr>
<th class="transactions__column-header transactions__column-header--status">
<span class="transactions__column-title transactions__column-title--status"><%= gettext "Status" %></span>
</th>
<th class="transactions__column-header"><%= gettext "Hash" %></th>
<th class="transactions__column-header transactions__column-header--optional"><%= gettext "Block" %></th>
<th class="transactions__column-header"><%= gettext "Age" %></th>
<th class="transactions__column-header transactions__column-header--optional"><%= gettext "From" %></th>
<th class="transactions__column-header transactions__column-header--optional"><%= gettext "To" %></th>
<th class="transactions__column-header"><%= gettext "Value" %></th>
</tr>
</thead>
<tbody>
<%= for transaction <- @page.entries do %>
<tr class="transactions__row">
<td class="transactions__column transactions__column--status">
<div class="transactions__dot transactions__dot--<%= status(transaction) %>"></div>
</td>
<td class="transactions__column transactions__column--hash">
<div class="transactions__hash">
<%= link(
transaction.hash,
class: "transactions__link transactions__link--truncated transactions__link--long-hash",
to: transaction_path(@conn, :show, @conn.assigns.locale, transaction.hash)
) %>
</div>
</td>
<td class="transactions__column transactions__column--block transactions__column--optional">
<%= link(
transaction.block.number,
class: "transactions__link",
to: block_path(@conn, :show, @conn.assigns.locale, transaction.block.number)
) %>
</td>
<td class="transactions__column transactions__column--age">
<%= transaction.block.timestamp |> Timex.from_now %>
</td>
<td class="transactions__column transactions__column--from transactions__column--optional">
<div class="transactions__hash">
<%= link(
transaction.from_address.hash,
class: "transactions__link transactions__link--truncated transactions__link--hash",
to: address_path(@conn, :show, @conn.assigns.locale, transaction.from_address.hash)
) %>
</div>
</td>
<td class="transactions__column transactions__column--to transactions__column--optional">
<div class="transactions__hash">
<%= link(
transaction.to_address.hash,
class: "transactions__link transactions__link--truncated transactions__link--hash",
to: address_path(@conn, :show, @conn.assigns.locale, transaction.to_address.hash)
) %>
</div>
</td>
<td class="transactions__column transactions__column--value"><%= value(transaction) %> <%= gettext "Ether" %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</section>

@ -1,8 +0,0 @@
defmodule ExplorerWeb.AddressTransactionFromView do
use ExplorerWeb, :view
alias ExplorerWeb.TransactionView
defdelegate status(transacton), to: TransactionView
defdelegate value(transaction), to: TransactionView
end

@ -1,8 +0,0 @@
defmodule ExplorerWeb.AddressTransactionToView do
use ExplorerWeb, :view
alias ExplorerWeb.TransactionView
defdelegate status(transacton), to: TransactionView
defdelegate value(transaction), to: TransactionView
end

@ -0,0 +1,19 @@
defmodule ExplorerWeb.AddressTransactionView do
use ExplorerWeb, :view
alias ExplorerWeb.{AddressView, TransactionView}
defdelegate balance(address), to: AddressView
defdelegate fee(transaction), to: TransactionView
def format_current_filter(filter) do
case filter do
"to" -> gettext("To")
"from" -> gettext("From")
_ -> gettext("All")
end
end
defdelegate status(transacton), to: TransactionView
defdelegate value(transaction), to: TransactionView
end

@ -1,13 +1,16 @@
defmodule ExplorerWeb.AddressView do
use ExplorerWeb, :view
@dialyzer :no_match
def format_balance(nil), do: "0"
alias Explorer.Chain
@dialyzer :no_match
def format_balance(balance) do
balance
|> Decimal.new()
|> Decimal.div(Decimal.new(1_000_000_000_000_000_000))
|> Decimal.to_string(:normal)
def balance(address) do
address
|> Chain.balance(:ether)
|> case do
nil -> ""
ether -> Cldr.Number.to_string!(ether, fractional_digits: 18)
end
end
end

@ -22,6 +22,18 @@ defmodule ExplorerWeb.TransactionView do
end
end
def fee(transaction) do
transaction
|> Chain.fee(:ether)
|> case do
{:actual, actual} ->
Cldr.Number.to_string!(actual, fractional_digits: 18)
{:maximum, maximum} ->
"<= " <> Cldr.Number.to_string!(maximum, fractional_digits: 18)
end
end
def first_seen(%Transaction{inserted_at: inserted_at}) do
Timex.from_now(inserted_at)
end

@ -1,5 +1,4 @@
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:52
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:51
#: lib/explorer_web/templates/address_transaction/index.html.eex:73
#: lib/explorer_web/templates/block/index.html.eex:30
#: lib/explorer_web/templates/block_transaction/index.html.eex:43
#: lib/explorer_web/templates/chain/show.html.eex:65
@ -9,8 +8,7 @@
msgid "Age"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:51
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:50
#: lib/explorer_web/templates/address_transaction/index.html.eex:72
#: lib/explorer_web/templates/block_transaction/index.html.eex:42
#: lib/explorer_web/templates/chain/show.html.eex:28
#: lib/explorer_web/templates/chain/show.html.eex:100
@ -33,8 +31,7 @@ msgstr ""
msgid "Gas Used"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:50
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:49
#: lib/explorer_web/templates/address_transaction/index.html.eex:71
#: lib/explorer_web/templates/block/show.html.eex:40
#: lib/explorer_web/templates/block_transaction/index.html.eex:41
#: lib/explorer_web/templates/chain/show.html.eex:99
@ -53,6 +50,7 @@ msgstr ""
msgid "POA Network Explorer"
msgstr ""
#: lib/explorer_web/templates/address_transaction/index.html.eex:23
#: lib/explorer_web/templates/block/index.html.eex:31
#: lib/explorer_web/templates/block/show.html.eex:16
#: lib/explorer_web/templates/block/show.html.eex:34
@ -65,8 +63,7 @@ msgstr ""
msgid "Transactions"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:55
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:54
#: lib/explorer_web/templates/address_transaction/index.html.eex:76
#: lib/explorer_web/templates/block_transaction/index.html.eex:46
#: lib/explorer_web/templates/chain/show.html.eex:102
#: lib/explorer_web/templates/pending_transaction/index.html.eex:38
@ -156,40 +153,39 @@ msgstr ""
msgid "%{count} transactions in this block"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:3
#: lib/explorer_web/templates/address_transaction/index.html.eex:3
#: lib/explorer_web/templates/transaction_log/index.html.eex:26
msgid "Address"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:53
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:52
#: lib/explorer_web/templates/address_transaction/index.html.eex:52
#: lib/explorer_web/templates/address_transaction/index.html.eex:74
#: lib/explorer_web/templates/block_transaction/index.html.eex:44
#: lib/explorer_web/templates/pending_transaction/index.html.eex:36
#: lib/explorer_web/templates/transaction/index.html.eex:37
#: lib/explorer_web/templates/transaction/overview.html.eex:47
#: lib/explorer_web/templates/transaction/show.html.eex:26
#: lib/explorer_web/views/address_transaction_view.ex:12
msgid "From"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:10
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:23
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:22
#: lib/explorer_web/templates/block/show.html.eex:9
#: lib/explorer_web/templates/block_transaction/index.html.eex:21
msgid "Overview"
msgstr ""
#: lib/explorer_web/views/transaction_view.ex:72
#: lib/explorer_web/views/transaction_view.ex:84
msgid "Success"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:54
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:53
#: lib/explorer_web/templates/address_transaction/index.html.eex:41
#: lib/explorer_web/templates/address_transaction/index.html.eex:75
#: lib/explorer_web/templates/block_transaction/index.html.eex:45
#: lib/explorer_web/templates/pending_transaction/index.html.eex:37
#: lib/explorer_web/templates/transaction/index.html.eex:38
#: lib/explorer_web/templates/transaction/overview.html.eex:61
#: lib/explorer_web/templates/transaction/show.html.eex:27
#: lib/explorer_web/views/address_transaction_view.ex:11
msgid "To"
msgstr ""
@ -202,7 +198,7 @@ msgstr ""
msgid "Transaction Status"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:29
#: lib/explorer_web/templates/address_transaction/index.html.eex:10
msgid "Balance"
msgstr ""
@ -235,9 +231,9 @@ msgstr ""
#: lib/explorer_web/templates/transaction/overview.html.eex:56
#: lib/explorer_web/templates/transaction/overview.html.eex:70
#: lib/explorer_web/views/transaction_view.ex:20
#: lib/explorer_web/views/transaction_view.ex:35
#: lib/explorer_web/views/transaction_view.ex:42
#: lib/explorer_web/views/transaction_view.ex:71
#: lib/explorer_web/views/transaction_view.ex:47
#: lib/explorer_web/views/transaction_view.ex:54
#: lib/explorer_web/views/transaction_view.ex:83
msgid "Pending"
msgstr ""
@ -300,24 +296,22 @@ msgstr ""
msgid "Next Page"
msgstr ""
#: lib/explorer_web/views/transaction_view.ex:69
#: lib/explorer_web/views/transaction_view.ex:81
msgid "Failed"
msgstr ""
#: lib/explorer_web/views/transaction_view.ex:70
#: lib/explorer_web/views/transaction_view.ex:82
msgid "Out of Gas"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:48
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:47
#: lib/explorer_web/templates/address_transaction/index.html.eex:69
#: lib/explorer_web/templates/block_transaction/index.html.eex:39
#: lib/explorer_web/templates/pending_transaction/index.html.eex:31
#: lib/explorer_web/templates/transaction/index.html.eex:31
msgid "Status"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:3
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:3
#: lib/explorer_web/templates/address_transaction/index.html.eex:3
msgid "Address %{number}"
msgstr ""
@ -325,9 +319,9 @@ msgstr ""
msgid "Showing #%{number}"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:31
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:101
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:101
#: lib/explorer_web/templates/address_transaction/index.html.eex:12
#: lib/explorer_web/templates/address_transaction/index.html.eex:124
#: lib/explorer_web/templates/address_transaction/index.html.eex:127
#: lib/explorer_web/templates/block_transaction/index.html.eex:90
#: lib/explorer_web/templates/chain/show.html.eex:128
#: lib/explorer_web/templates/pending_transaction/index.html.eex:80
@ -378,3 +372,12 @@ msgstr ""
#: lib/explorer_web/templates/transaction/overview.html.eex:105
msgid "Wei"
msgstr ""
#: lib/explorer_web/templates/address_transaction/index.html.eex:36
#: lib/explorer_web/views/address_transaction_view.ex:13
msgid "All"
msgstr ""
#: lib/explorer_web/templates/address_transaction/index.html.eex:77
msgid "Fee"
msgstr ""

@ -10,8 +10,7 @@ msgid ""
msgstr ""
"Language: en\n"
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:52
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:51
#: lib/explorer_web/templates/address_transaction/index.html.eex:73
#: lib/explorer_web/templates/block/index.html.eex:30
#: lib/explorer_web/templates/block_transaction/index.html.eex:43
#: lib/explorer_web/templates/chain/show.html.eex:65
@ -21,8 +20,7 @@ msgstr ""
msgid "Age"
msgstr "Age"
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:51
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:50
#: lib/explorer_web/templates/address_transaction/index.html.eex:72
#: lib/explorer_web/templates/block_transaction/index.html.eex:42
#: lib/explorer_web/templates/chain/show.html.eex:28
#: lib/explorer_web/templates/chain/show.html.eex:100
@ -45,8 +43,7 @@ msgstr "%{year} POA Network Ltd. All rights reserved"
msgid "Gas Used"
msgstr "Gas Used"
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:50
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:49
#: lib/explorer_web/templates/address_transaction/index.html.eex:71
#: lib/explorer_web/templates/block/show.html.eex:40
#: lib/explorer_web/templates/block_transaction/index.html.eex:41
#: lib/explorer_web/templates/chain/show.html.eex:99
@ -65,6 +62,7 @@ msgstr "Height"
msgid "POA Network Explorer"
msgstr "POA Network Explorer"
#: lib/explorer_web/templates/address_transaction/index.html.eex:23
#: lib/explorer_web/templates/block/index.html.eex:31
#: lib/explorer_web/templates/block/show.html.eex:16
#: lib/explorer_web/templates/block/show.html.eex:34
@ -77,8 +75,7 @@ msgstr "POA Network Explorer"
msgid "Transactions"
msgstr "Transactions"
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:55
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:54
#: lib/explorer_web/templates/address_transaction/index.html.eex:76
#: lib/explorer_web/templates/block_transaction/index.html.eex:46
#: lib/explorer_web/templates/chain/show.html.eex:102
#: lib/explorer_web/templates/pending_transaction/index.html.eex:38
@ -168,40 +165,39 @@ msgstr "%{confirmations} block confirmations"
msgid "%{count} transactions in this block"
msgstr "%{count} transactions in this block"
#: lib/explorer_web/templates/address/show.html.eex:3
#: lib/explorer_web/templates/address_transaction/index.html.eex:3
#: lib/explorer_web/templates/transaction_log/index.html.eex:26
msgid "Address"
msgstr "Address"
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:53
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:52
#: lib/explorer_web/templates/address_transaction/index.html.eex:52
#: lib/explorer_web/templates/address_transaction/index.html.eex:74
#: lib/explorer_web/templates/block_transaction/index.html.eex:44
#: lib/explorer_web/templates/pending_transaction/index.html.eex:36
#: lib/explorer_web/templates/transaction/index.html.eex:37
#: lib/explorer_web/templates/transaction/overview.html.eex:47
#: lib/explorer_web/templates/transaction/show.html.eex:26
#: lib/explorer_web/views/address_transaction_view.ex:12
msgid "From"
msgstr "From"
#: lib/explorer_web/templates/address/show.html.eex:10
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:23
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:22
#: lib/explorer_web/templates/block/show.html.eex:9
#: lib/explorer_web/templates/block_transaction/index.html.eex:21
msgid "Overview"
msgstr "Overview"
#: lib/explorer_web/views/transaction_view.ex:72
#: lib/explorer_web/views/transaction_view.ex:84
msgid "Success"
msgstr "Success"
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:54
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:53
#: lib/explorer_web/templates/address_transaction/index.html.eex:41
#: lib/explorer_web/templates/address_transaction/index.html.eex:75
#: lib/explorer_web/templates/block_transaction/index.html.eex:45
#: lib/explorer_web/templates/pending_transaction/index.html.eex:37
#: lib/explorer_web/templates/transaction/index.html.eex:38
#: lib/explorer_web/templates/transaction/overview.html.eex:61
#: lib/explorer_web/templates/transaction/show.html.eex:27
#: lib/explorer_web/views/address_transaction_view.ex:11
msgid "To"
msgstr "To"
@ -214,7 +210,7 @@ msgstr "Transaction Hash"
msgid "Transaction Status"
msgstr "Transaction Status"
#: lib/explorer_web/templates/address/show.html.eex:29
#: lib/explorer_web/templates/address_transaction/index.html.eex:10
msgid "Balance"
msgstr "Balance"
@ -247,9 +243,9 @@ msgstr "Showing %{count} Transactions"
#: lib/explorer_web/templates/transaction/overview.html.eex:56
#: lib/explorer_web/templates/transaction/overview.html.eex:70
#: lib/explorer_web/views/transaction_view.ex:20
#: lib/explorer_web/views/transaction_view.ex:35
#: lib/explorer_web/views/transaction_view.ex:42
#: lib/explorer_web/views/transaction_view.ex:71
#: lib/explorer_web/views/transaction_view.ex:47
#: lib/explorer_web/views/transaction_view.ex:54
#: lib/explorer_web/views/transaction_view.ex:83
msgid "Pending"
msgstr "Pending"
@ -312,24 +308,22 @@ msgstr ""
msgid "Next Page"
msgstr ""
#: lib/explorer_web/views/transaction_view.ex:69
#: lib/explorer_web/views/transaction_view.ex:81
msgid "Failed"
msgstr ""
#: lib/explorer_web/views/transaction_view.ex:70
#: lib/explorer_web/views/transaction_view.ex:82
msgid "Out of Gas"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:48
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:47
#: lib/explorer_web/templates/address_transaction/index.html.eex:69
#: lib/explorer_web/templates/block_transaction/index.html.eex:39
#: lib/explorer_web/templates/pending_transaction/index.html.eex:31
#: lib/explorer_web/templates/transaction/index.html.eex:31
msgid "Status"
msgstr ""
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:3
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:3
#: lib/explorer_web/templates/address_transaction/index.html.eex:3
msgid "Address %{number}"
msgstr ""
@ -337,9 +331,9 @@ msgstr ""
msgid "Showing #%{number}"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:31
#: lib/explorer_web/templates/address_transaction_from/index.html.eex:101
#: lib/explorer_web/templates/address_transaction_to/index.html.eex:101
#: lib/explorer_web/templates/address_transaction/index.html.eex:12
#: lib/explorer_web/templates/address_transaction/index.html.eex:124
#: lib/explorer_web/templates/address_transaction/index.html.eex:127
#: lib/explorer_web/templates/block_transaction/index.html.eex:90
#: lib/explorer_web/templates/chain/show.html.eex:128
#: lib/explorer_web/templates/pending_transaction/index.html.eex:80
@ -390,3 +384,12 @@ msgstr ""
#: lib/explorer_web/templates/transaction/overview.html.eex:105
msgid "Wei"
msgstr ""
#: lib/explorer_web/templates/address_transaction/index.html.eex:36
#: lib/explorer_web/views/address_transaction_view.ex:13
msgid "All"
msgstr ""
#: lib/explorer_web/templates/address_transaction/index.html.eex:77
msgid "Fee"
msgstr ""

@ -4,20 +4,14 @@ defmodule ExplorerWeb.AddressControllerTest do
alias Explorer.Chain.{Credit, Debit}
describe "GET show/3" do
test "without address returns not found", %{conn: conn} do
conn = get(conn, "/en/addresses/unknown")
assert html_response(conn, 404)
end
test "with address returns an address", %{conn: conn} do
address = insert(:address, hash: "0x9")
test "redirects to addresses/:address_id/transactions", %{conn: conn} do
insert(:address, hash: "0x9")
Credit.refresh()
Debit.refresh()
conn = get(conn, "/en/addresses/0x9")
assert conn.assigns.address.id == address.id
assert redirected_to(conn) =~ "/en/addresses/0x9/transactions"
end
end
end

@ -1,11 +1,11 @@
defmodule ExplorerWeb.AddressTransactionToControllerTest do
defmodule ExplorerWeb.AddressTransactionControllerTest do
use ExplorerWeb.ConnCase
import ExplorerWeb.Router.Helpers, only: [address_transaction_to_path: 4]
import ExplorerWeb.Router.Helpers, only: [address_transaction_path: 4]
describe "GET index/2" do
test "without address", %{conn: conn} do
conn = get(conn, address_transaction_to_path(conn, :index, :en, "unknown"))
conn = get(conn, address_transaction_path(conn, :index, :en, "unknown"))
assert html_response(conn, 404)
end
@ -18,7 +18,7 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
block = insert(:block)
insert(:block_transaction, transaction: transaction, block: block)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn = get(conn, address_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 1
@ -42,7 +42,7 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
insert(:to_address, transaction: transaction, address: other_address)
insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn = get(conn, address_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0
@ -56,7 +56,7 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn = get(conn, address_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0
@ -70,7 +70,7 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
address = insert(:address)
insert(:to_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn = get(conn, address_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0
@ -84,7 +84,7 @@ defmodule ExplorerWeb.AddressTransactionToControllerTest do
address = insert(:address)
insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_to_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
conn = get(conn, address_transaction_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0

@ -1,92 +0,0 @@
defmodule ExplorerWeb.AddressTransactionFromControllerTest do
use ExplorerWeb.ConnCase
import ExplorerWeb.Router.Helpers, only: [address_transaction_from_path: 4]
describe "GET index/2" do
test "without address", %{conn: conn} do
conn = get(conn, address_transaction_from_path(conn, :index, :en, "unknown"))
assert html_response(conn, 404)
end
test "returns transactions from this address", %{conn: conn} do
address = insert(:address)
hash = "0xsnacks"
transaction = insert(:transaction, hash: hash, from_address_id: address.id)
insert(:receipt, transaction: transaction)
block = insert(:block)
insert(:block_transaction, transaction: transaction, block: block)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
transaction_hash_divs = Floki.find(html, "td.transactions__column--hash div.transactions__hash a")
assert length(transaction_hash_divs) == 1
assert List.first(transaction_hash_divs) |> Floki.attribute("href") == [
"/en/transactions/#{hash}"
]
end
test "does not return transactions to this address", %{conn: conn} do
transaction = insert(:transaction, hash: "0xsnacks")
insert(:receipt, transaction: transaction)
block = insert(:block)
insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address)
other_address = insert(:address)
insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: other_address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0
end
test "does not return related transactions without a receipt", %{conn: conn} do
transaction = insert(:transaction)
block = insert(:block)
insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address)
insert(:to_address, transaction: transaction, address: address)
insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0
end
test "does not return related transactions without a from address", %{conn: conn} do
transaction = insert(:transaction)
insert(:receipt, transaction: transaction)
block = insert(:block)
insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address)
insert(:to_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0
end
test "does not return related transactions without a to address", %{conn: conn} do
transaction = insert(:transaction)
insert(:receipt, transaction: transaction)
block = insert(:block)
insert(:block_transaction, transaction: transaction, block: block)
address = insert(:address)
insert(:from_address, transaction: transaction, address: address)
conn = get(conn, address_transaction_from_path(ExplorerWeb.Endpoint, :index, :en, address.hash))
assert html = html_response(conn, 200)
assert html |> Floki.find("tbody tr") |> length == 0
end
end
end

@ -105,101 +105,158 @@ defmodule ExplorerWeb.UserListTest do
|> assert_has(css(".transactions__link--long-hash", text: "0xfaschtnacht"))
end
test "views transactions", %{session: session} do
block =
insert(:block, %{
number: 555,
timestamp: Timex.now() |> Timex.shift(hours: -2),
gas_used: 123_987
})
for _ <- 0..3, do: insert(:transaction) |> with_block(block)
insert(:transaction, hash: "0xC001", gas: 5891) |> with_block
lincoln = insert(:address, hash: "0xlincoln")
taft = insert(:address, hash: "0xhowardtaft")
transaction =
insert(
:transaction,
hash: "0xSk8",
value: Explorer.Chain.Wei.from(Decimal.new(5656), :ether),
gas: Decimal.new(1_230_000_000_000_123_123),
gas_price: Decimal.new(7_890_000_000_898_912_300_045),
input: "0x00012",
nonce: 99045,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"),
from_address_id: taft.id,
to_address_id: lincoln.id
)
insert(:block_transaction, block: block, transaction: transaction)
receipt = insert(:receipt, transaction: transaction, status: 1)
insert(:log, address_id: lincoln.id, receipt: receipt)
# From Lincoln to Taft.
txn_from_lincoln =
insert(
:transaction,
hash: "0xrazerscooter",
from_address_id: lincoln.id,
to_address_id: taft.id
)
insert(:block_transaction, block: block, transaction: txn_from_lincoln)
insert(:receipt, transaction: txn_from_lincoln)
internal = insert(:internal_transaction, transaction_id: transaction.id)
Credit.refresh()
Debit.refresh()
session
|> visit("/en")
|> assert_has(css(".transactions__title", text: "Transactions"))
|> assert_has(css(".transactions__column--hash", count: 5))
|> assert_has(css(".transactions__column--value", count: 5))
|> assert_has(css(".transactions__column--age", count: 5, visible: false))
|> visit("/transactions")
|> click(css(".transactions__tab-link", text: "Pending"))
|> click(css(".transactions__link", text: "0xC001"))
|> assert_has(css(".transaction__item-value--status", text: "Pending"))
|> visit("/transactions")
|> refute_has(css(".transactions__column--block", text: "Pending"))
|> click(link("0xSk8"))
|> assert_has(css(".transaction__subheading", text: "0xSk8"))
|> assert_has(css(".transaction__item", text: "123,987"))
|> assert_has(css(".transaction__item", text: "5,656 POA"))
|> assert_has(css(".transaction__item", text: "Success"))
|> assert_has(
css(
".transaction__item",
text: "7,890,000,000,898,912,300,045 Wei (7,890,000,000,898.912 Gwei)"
describe "transactions and address pages" do
setup do
block =
insert(:block, %{
number: 555,
timestamp: Timex.now() |> Timex.shift(hours: -2),
gas_used: 123_987
})
for _ <- 0..3, do: insert(:transaction) |> with_block(block)
insert(:transaction, hash: "0xC001", gas: 5891) |> with_block
lincoln = insert(:address, hash: "0xlincoln")
taft = insert(:address, hash: "0xhowardtaft")
transaction =
insert(
:transaction,
hash: "0xSk8",
value: Explorer.Chain.Wei.from(Decimal.new(5656), :ether),
gas: Decimal.new(1_230_000_000_000_123_123),
gas_price: Decimal.new(7_890_000_000_898_912_300_045),
input: "0x00012",
nonce: 99045,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"),
from_address_id: taft.id,
to_address_id: lincoln.id
)
insert(:block_transaction, block: block, transaction: transaction)
receipt = insert(:receipt, transaction: transaction, status: 1)
insert(:log, address_id: lincoln.id, receipt: receipt)
# From Lincoln to Taft.
txn_from_lincoln =
insert(
:transaction,
hash: "0xrazerscooter",
from_address_id: lincoln.id,
to_address_id: taft.id
)
insert(:block_transaction, block: block, transaction: txn_from_lincoln)
insert(:receipt, transaction: txn_from_lincoln)
internal = insert(:internal_transaction, transaction_id: transaction.id)
Credit.refresh()
Debit.refresh()
{:ok, %{internal: internal}}
end
test "views transactions", %{session: session} do
session
|> visit("/en")
|> assert_has(css(".transactions__title", text: "Transactions"))
|> assert_has(css(".transactions__column--hash", count: 5))
|> assert_has(css(".transactions__column--value", count: 5))
|> assert_has(css(".transactions__column--age", count: 5, visible: false))
end
test "can see pending transactions", %{session: session} do
session
|> visit("/transactions")
|> click(css(".transactions__tab-link", text: "Pending"))
|> click(css(".transactions__link", text: "0xC001"))
|> assert_has(css(".transaction__item-value--status", text: "Pending"))
end
test "don't see pending transactions by default", %{session: session} do
session
|> visit("/transactions")
|> refute_has(css(".transactions__column--block", text: "Pending"))
end
test "can see a transaction's details", %{session: session} do
session
|> visit("/transactions")
|> click(link("0xSk8"))
|> assert_has(css(".transaction__subheading", text: "0xSk8"))
|> assert_has(css(".transaction__item", text: "123,987"))
|> assert_has(css(".transaction__item", text: "5,656 POA"))
|> assert_has(css(".transaction__item", text: "Success"))
|> assert_has(
css(
".transaction__item",
text: "7,890,000,000,898,912,300,045 Wei (7,890,000,000,898.912 Gwei)"
)
)
)
|> assert_has(css(".transaction__item", text: "1,230,000,000,000,123,123 Gas"))
|> assert_has(css(".transaction__item", text: "0x00012"))
|> assert_has(css(".transaction__item", text: "99045"))
|> assert_has(css(".transaction__item", text: "123,987"))
|> assert_has(css(".transaction__item", text: "0xlincoln"))
|> assert_has(css(".transaction__item", text: "0xhowardtaft"))
|> assert_has(css(".transaction__item", text: "block confirmations"))
|> assert_has(css(".transaction__item", text: "48 years ago"))
|> assert_has(css(".transaction__item", text: "38 years ago"))
|> click(link("Internal Transactions"))
|> assert_has(css(".internal-transaction__table", text: internal.call_type))
|> visit("/en/transactions/0xSk8")
|> click(link("Logs"))
|> assert_has(css(".transaction-log__link", text: "0xlincoln"))
|> click(css(".transaction-log__link", text: "0xlincoln"))
|> assert_has(css(".address__subheading", text: "0xlincoln"))
|> click(css(".address__link", text: "Transactions To"))
|> assert_has(css(".transactions__link--long-hash", text: "0xSk8"))
|> click(css(".address__link", text: "Transactions From"))
|> assert_has(css(".transactions__link--long-hash", text: "0xrazerscooter"))
|> assert_has(css(".transaction__item", text: "1,230,000,000,000,123,123 Gas"))
|> assert_has(css(".transaction__item", text: "0x00012"))
|> assert_has(css(".transaction__item", text: "99045"))
|> assert_has(css(".transaction__item", text: "123,987"))
|> assert_has(css(".transaction__item", text: "0xlincoln"))
|> assert_has(css(".transaction__item", text: "0xhowardtaft"))
|> assert_has(css(".transaction__item", text: "block confirmations"))
|> assert_has(css(".transaction__item", text: "49 years ago"))
|> assert_has(css(".transaction__item", text: "38 years ago"))
end
test "can see internal transactions for a transaction", %{
session: session,
internal: internal
} do
session
|> visit("/en/transactions/0xSk8")
|> click(link("Internal Transactions"))
|> assert_has(css(".internal-transaction__table", text: internal.call_type))
end
test "can view a transaction's logs", %{session: session} do
session
|> visit("/en/transactions/0xSk8")
|> click(link("Logs"))
|> assert_has(css(".transaction-log__link", text: "0xlincoln"))
end
test "can visit an address from the transaction logs page", %{session: session} do
session
|> visit("/en/transactions/0xSk8/logs")
|> click(css(".transaction-log__link", text: "0xlincoln"))
|> assert_has(css(".address__subheading", text: "0xlincoln"))
end
test "see's all addresses transactions by default", %{session: session} do
session
|> visit("/en/addresses/0xlincoln")
|> assert_has(css(".transactions__link--long-hash", text: "0xSk8"))
|> assert_has(css(".transactions__link--long-hash", text: "0xrazerscooter"))
end
test "can filter to only see transactions to an address", %{session: session} do
session
|> visit("/en/addresses/0xlincoln")
|> click(css("[data-test='filterDropdown']", text: "Filter: All"))
|> click(css(".address__link", text: "To"))
|> assert_has(css(".transactions__link--long-hash", text: "0xSk8"))
|> refute_has(css(".transactions__link--long-hash", text: "0xrazerscooter"))
end
test "can filter to only see transactions from an address", %{session: session} do
session
|> visit("/en/addresses/0xlincoln")
|> click(css("[data-test='filterDropdown']", text: "Filter: All"))
|> click(css(".address__link", text: "From"))
|> assert_has(css(".transactions__link--long-hash", text: "0xrazerscooter"))
|> refute_has(css(".transactions__link--long-hash", text: "0xSk8"))
end
end
test "views addresses", %{session: session} do
@ -207,6 +264,6 @@ defmodule ExplorerWeb.UserListTest do
session
|> visit("/en/addresses/0xthinmints")
|> assert_has(css(".address__balance", text: "0.0000000000000005"))
|> assert_has(css(".address__balance", text: "0.000,000,000,000,000,500 POA"))
end
end

@ -0,0 +1,60 @@
defmodule ExplorerWeb.AddressTransactionViewTest do
use Explorer.DataCase
alias ExplorerWeb.AddressTransactionView
describe "fee/0" do
test "formats the fee for a successful transaction" do
insert(:block, number: 24)
time = Timex.now() |> Timex.shift(hours: -2)
block =
insert(:block, %{
number: 1,
gas_used: 99523,
timestamp: time
})
to_address = insert(:address, hash: "0xsleepypuppy")
from_address = insert(:address, hash: "0xilovefrogs")
transaction =
insert(
:transaction,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"),
to_address_id: to_address.id,
from_address_id: from_address.id,
gas_price: Decimal.new(1_000_000_000.0)
)
|> with_block(block)
insert(:receipt, status: 1, gas_used: Decimal.new(435_334), transaction: transaction)
transaction =
transaction
|> Repo.preload([:receipt])
assert AddressTransactionView.fee(transaction) == "0.000,435,334,000,000,000"
end
test "fee returns max_gas for pending transaction" do
to_address = insert(:address, hash: "0xchadmuska")
from_address = insert(:address, hash: "0xtonyhawk")
transaction =
insert(
:transaction,
inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"),
updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"),
to_address_id: to_address.id,
from_address_id: from_address.id,
gas: Decimal.new(21000.0),
gas_price: Decimal.new(1_000_000_000.0)
)
|> Repo.preload([:to_address, :from_address, :receipt])
assert AddressTransactionView.fee(transaction) == "<= 0.000,021,000,000,000,000"
end
end
end
Loading…
Cancel
Save