going somewhere
This commit is contained in:
1 parent
b972e6e69f
commit
10b054db25
31 files changed
+620
-301
No files matched your search
@@ -39,6 +39,7 @@ file should be self documented.
|
|||||||
* [clojure](https://clojure.org/) for programming language
|
* [clojure](https://clojure.org/) for programming language
|
||||||
* [kafka](http://kafka.apache.org/) for message broker/database
|
* [kafka](http://kafka.apache.org/) for message broker/database
|
||||||
* [datomic-free](https://www.datomic.com/) for transaction needs
|
* [datomic-free](https://www.datomic.com/) for transaction needs
|
||||||
|
* [clickhouse](https://clickhouse.yandex) to store the market data
|
||||||
|
|
||||||
#### In the browser
|
#### In the browser
|
||||||
* [clojure script](https://clojurescript.org/) for programming language
|
* [clojure script](https://clojurescript.org/) for programming language
|
||||||
|
|||||||
@@ -159,6 +159,14 @@ services:
|
|||||||
- "4335:4335"
|
- "4335:4335"
|
||||||
- "4336:4336"
|
- "4336:4336"
|
||||||
|
|
||||||
|
clickhouse:
|
||||||
|
image: "yandex/clickhouse-server:18.14"
|
||||||
|
restart: "unless-stopped"
|
||||||
|
ports:
|
||||||
|
- "8123:8123"
|
||||||
|
- "9000:9000"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# skirmish itself
|
# skirmish itself
|
||||||
|
|
||||||
|
|||||||
+2
-4
@@ -1,7 +1,6 @@
|
|||||||
(ns skirmish.wui.middleware
|
(ns skirmish.wui.middleware
|
||||||
(:require [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
|
(:require [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
|
||||||
[prone.middleware :refer [wrap-exceptions]]
|
[prone.middleware :refer [wrap-exceptions]]))
|
||||||
[ring.middleware.reload :refer [wrap-reload]]))
|
|
||||||
|
|
||||||
(defn custom-dev-middleware [handler]
|
(defn custom-dev-middleware [handler]
|
||||||
(fn [request]
|
(fn [request]
|
||||||
@@ -12,5 +11,4 @@
|
|||||||
(-> handler
|
(-> handler
|
||||||
(wrap-defaults site-defaults)
|
(wrap-defaults site-defaults)
|
||||||
custom-dev-middleware
|
custom-dev-middleware
|
||||||
wrap-exceptions
|
wrap-exceptions))
|
||||||
wrap-reload))
|
|
||||||
Generated
+3
-3
@@ -5,9 +5,9 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bulma": {
|
"bulma": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.2.tgz",
|
||||||
"integrity": "sha512-wRSO2LXB+qI9Pyz2id+uZr4quz5aftSN7Ay1ysr1+krzVp3utD+Ci4CeKuZdrYGc800t65b7heXBL6qw2Wo/lQ=="
|
"integrity": "sha512-6JHEu8U/1xsyOst/El5ImLcZIiE2JFXgvrz8GGWbnDLwTNRPJzdAM0aoUM1Ns0avALcVb6KZz9NhzmU53dGDcQ=="
|
||||||
},
|
},
|
||||||
"font-awesome": {
|
"font-awesome": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
:java-source-paths ["src/java"]
|
:java-source-paths ["src/java"]
|
||||||
|
|
||||||
:npm {:dependencies [[bulma "0.7.1"]
|
:npm {:dependencies [[bulma "0.7.2"]
|
||||||
[font-awesome "4.7.0"]]}
|
[font-awesome "4.7.0"]]}
|
||||||
|
|
||||||
:ring {:handler skirmish.wui.handler/app
|
:ring {:handler skirmish.wui.handler/app
|
||||||
|
|||||||
@@ -38,14 +38,10 @@
|
|||||||
:ip "0.0.0.0"
|
:ip "0.0.0.0"
|
||||||
:max-body 524288 ;; 512kb, small but enough
|
:max-body 524288 ;; 512kb, small but enough
|
||||||
:max-ws 524288
|
:max-ws 524288
|
||||||
|
:default-sec-id :dapm
|
||||||
:datomic {:transactor-url "datomic:free://localhost:4334/skirmish.broker"}}
|
:datomic {:transactor-url "datomic:free://localhost:4334/skirmish.broker"}}
|
||||||
|
|
||||||
:wui
|
:wui
|
||||||
{:port #long #or [#env WUI_PORT 3000]}
|
{:port #long #or [#env WUI_PORT 3000]}}
|
||||||
|
|
||||||
;; stupid parameters about benchmarking tools
|
|
||||||
:benchmark {
|
|
||||||
:number-of-requests-per-securities 30}}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+381
-54
@@ -14,7 +14,7 @@
|
|||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
user-select: none; }
|
user-select: none; }
|
||||||
|
|
||||||
.select:not(.is-multiple):not(.is-loading)::after, .navbar-link::after {
|
.select:not(.is-multiple):not(.is-loading)::after, .navbar-link:not(.is-arrowless)::after {
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
width: 0.625em; }
|
width: 0.625em; }
|
||||||
|
|
||||||
.box:not(:last-child), .content:not(:last-child), .notification:not(:last-child), .progress:not(:last-child), .table:not(:last-child), .table-container:not(:last-child), .title:not(:last-child),
|
.box:not(:last-child), .content:not(:last-child), .notification:not(:last-child), .progress:not(:last-child), .table:not(:last-child), .table-container:not(:last-child), .title:not(:last-child),
|
||||||
.subtitle:not(:last-child), .block:not(:last-child), .highlight:not(:last-child), .breadcrumb:not(:last-child), .level:not(:last-child), .message:not(:last-child), .tabs:not(:last-child) {
|
.subtitle:not(:last-child), .block:not(:last-child), .highlight:not(:last-child), .breadcrumb:not(:last-child), .level:not(:last-child), .list:not(:last-child), .message:not(:last-child), .tabs:not(:last-child) {
|
||||||
margin-bottom: 1.5rem; }
|
margin-bottom: 1.5rem; }
|
||||||
|
|
||||||
.delete, .modal-close {
|
.delete, .modal-close {
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 290486px;
|
border-radius: 290486px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -993,6 +994,16 @@ a.has-text-danger:hover, a.has-text-danger:focus {
|
|||||||
.is-hidden {
|
.is-hidden {
|
||||||
display: none !important; }
|
display: none !important; }
|
||||||
|
|
||||||
|
.is-sr-only {
|
||||||
|
border: none !important;
|
||||||
|
clip: rect(0, 0, 0, 0) !important;
|
||||||
|
height: 0.01em !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
position: absolute !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
width: 0.01em !important; }
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.is-hidden-mobile {
|
.is-hidden-mobile {
|
||||||
display: none !important; } }
|
display: none !important; } }
|
||||||
@@ -1081,10 +1092,10 @@ a.has-text-danger:hover, a.has-text-danger:focus {
|
|||||||
box-shadow: none !important; }
|
box-shadow: none !important; }
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
background-color: white;
|
background-color: #7a7a7a;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
|
box-shadow: 0 2px 3px #b5b5b5, 0 0 0 1px #b5b5b5;
|
||||||
color: whitesmoke;
|
color: white;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 1.25rem; }
|
padding: 1.25rem; }
|
||||||
|
|
||||||
@@ -1788,7 +1799,7 @@ a.box:active {
|
|||||||
justify-content: flex-start; }
|
justify-content: flex-start; }
|
||||||
.buttons .button {
|
.buttons .button {
|
||||||
margin-bottom: 0.5rem; }
|
margin-bottom: 0.5rem; }
|
||||||
.buttons .button:not(:last-child) {
|
.buttons .button:not(:last-child):not(.is-fullwidth) {
|
||||||
margin-right: 0.5rem; }
|
margin-right: 0.5rem; }
|
||||||
.buttons:last-child {
|
.buttons:last-child {
|
||||||
margin-bottom: -0.5rem; }
|
margin-bottom: -0.5rem; }
|
||||||
@@ -1903,9 +1914,19 @@ a.box:active {
|
|||||||
padding: 1.25em 1.5em; }
|
padding: 1.25em 1.5em; }
|
||||||
|
|
||||||
.content ol {
|
.content ol {
|
||||||
list-style: decimal outside;
|
list-style-position: outside;
|
||||||
margin-left: 2em;
|
margin-left: 2em;
|
||||||
margin-top: 1em; }
|
margin-top: 1em; }
|
||||||
|
.content ol:not([type]) {
|
||||||
|
list-style-type: decimal; }
|
||||||
|
.content ol:not([type]).is-lower-alpha {
|
||||||
|
list-style-type: lower-alpha; }
|
||||||
|
.content ol:not([type]).is-lower-roman {
|
||||||
|
list-style-type: lower-roman; }
|
||||||
|
.content ol:not([type]).is-upper-alpha {
|
||||||
|
list-style-type: upper-alpha; }
|
||||||
|
.content ol:not([type]).is-upper-roman {
|
||||||
|
list-style-type: upper-roman; }
|
||||||
|
|
||||||
.content ul {
|
.content ul {
|
||||||
list-style: disc outside;
|
list-style: disc outside;
|
||||||
@@ -2239,7 +2260,7 @@ a.box:active {
|
|||||||
.select select:not([multiple]) {
|
.select select:not([multiple]) {
|
||||||
padding-right: 2.5em; }
|
padding-right: 2.5em; }
|
||||||
.select select[multiple] {
|
.select select[multiple] {
|
||||||
height: initial;
|
height: auto;
|
||||||
padding: 0; }
|
padding: 0; }
|
||||||
.select select[multiple] option {
|
.select select[multiple] option {
|
||||||
padding: 0.5em 1em; }
|
padding: 0.5em 1em; }
|
||||||
@@ -2595,12 +2616,13 @@ a.box:active {
|
|||||||
border-color: #cfcfcf; }
|
border-color: #cfcfcf; }
|
||||||
|
|
||||||
.file-input {
|
.file-input {
|
||||||
height: 0.01em;
|
height: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 0.01em; }
|
width: 100%; }
|
||||||
|
|
||||||
.file-cta,
|
.file-cta,
|
||||||
.file-name {
|
.file-name {
|
||||||
@@ -2696,31 +2718,31 @@ a.box:active {
|
|||||||
.field.has-addons .control:last-child .select select {
|
.field.has-addons .control:last-child .select select {
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
border-top-left-radius: 0; }
|
border-top-left-radius: 0; }
|
||||||
.field.has-addons .control .button:hover, .field.has-addons .control .button.is-hovered,
|
.field.has-addons .control .button:not([disabled]):hover, .field.has-addons .control .button:not([disabled]).is-hovered,
|
||||||
.field.has-addons .control .input:hover,
|
.field.has-addons .control .input:not([disabled]):hover,
|
||||||
.field.has-addons .control .input.is-hovered,
|
.field.has-addons .control .input:not([disabled]).is-hovered,
|
||||||
.field.has-addons .control .select select:hover,
|
.field.has-addons .control .select select:not([disabled]):hover,
|
||||||
.field.has-addons .control .select select.is-hovered {
|
.field.has-addons .control .select select:not([disabled]).is-hovered {
|
||||||
z-index: 2; }
|
z-index: 2; }
|
||||||
.field.has-addons .control .button:focus, .field.has-addons .control .button.is-focused, .field.has-addons .control .button:active, .field.has-addons .control .button.is-active,
|
.field.has-addons .control .button:not([disabled]):focus, .field.has-addons .control .button:not([disabled]).is-focused, .field.has-addons .control .button:not([disabled]):active, .field.has-addons .control .button:not([disabled]).is-active,
|
||||||
.field.has-addons .control .input:focus,
|
.field.has-addons .control .input:not([disabled]):focus,
|
||||||
.field.has-addons .control .input.is-focused,
|
.field.has-addons .control .input:not([disabled]).is-focused,
|
||||||
.field.has-addons .control .input:active,
|
.field.has-addons .control .input:not([disabled]):active,
|
||||||
.field.has-addons .control .input.is-active,
|
.field.has-addons .control .input:not([disabled]).is-active,
|
||||||
.field.has-addons .control .select select:focus,
|
.field.has-addons .control .select select:not([disabled]):focus,
|
||||||
.field.has-addons .control .select select.is-focused,
|
.field.has-addons .control .select select:not([disabled]).is-focused,
|
||||||
.field.has-addons .control .select select:active,
|
.field.has-addons .control .select select:not([disabled]):active,
|
||||||
.field.has-addons .control .select select.is-active {
|
.field.has-addons .control .select select:not([disabled]).is-active {
|
||||||
z-index: 3; }
|
z-index: 3; }
|
||||||
.field.has-addons .control .button:focus:hover, .field.has-addons .control .button.is-focused:hover, .field.has-addons .control .button:active:hover, .field.has-addons .control .button.is-active:hover,
|
.field.has-addons .control .button:not([disabled]):focus:hover, .field.has-addons .control .button:not([disabled]).is-focused:hover, .field.has-addons .control .button:not([disabled]):active:hover, .field.has-addons .control .button:not([disabled]).is-active:hover,
|
||||||
.field.has-addons .control .input:focus:hover,
|
.field.has-addons .control .input:not([disabled]):focus:hover,
|
||||||
.field.has-addons .control .input.is-focused:hover,
|
.field.has-addons .control .input:not([disabled]).is-focused:hover,
|
||||||
.field.has-addons .control .input:active:hover,
|
.field.has-addons .control .input:not([disabled]):active:hover,
|
||||||
.field.has-addons .control .input.is-active:hover,
|
.field.has-addons .control .input:not([disabled]).is-active:hover,
|
||||||
.field.has-addons .control .select select:focus:hover,
|
.field.has-addons .control .select select:not([disabled]):focus:hover,
|
||||||
.field.has-addons .control .select select.is-focused:hover,
|
.field.has-addons .control .select select:not([disabled]).is-focused:hover,
|
||||||
.field.has-addons .control .select select:active:hover,
|
.field.has-addons .control .select select:not([disabled]):active:hover,
|
||||||
.field.has-addons .control .select select.is-active:hover {
|
.field.has-addons .control .select select:not([disabled]).is-active:hover {
|
||||||
z-index: 4; }
|
z-index: 4; }
|
||||||
.field.has-addons .control.is-expanded {
|
.field.has-addons .control.is-expanded {
|
||||||
flex-grow: 1; }
|
flex-grow: 1; }
|
||||||
@@ -2805,6 +2827,7 @@ a.box:active {
|
|||||||
margin-right: 0.75rem; } }
|
margin-right: 0.75rem; } }
|
||||||
|
|
||||||
.control {
|
.control {
|
||||||
|
clear: both;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: left; }
|
text-align: left; }
|
||||||
@@ -2963,7 +2986,7 @@ a.box:active {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 1.25rem 2.5rem 1.25rem 1.5rem;
|
padding: 1.25rem 2.5rem 1.25rem 1.5rem;
|
||||||
position: relative; }
|
position: relative; }
|
||||||
.notification a:not(.button) {
|
.notification a:not(.button):not(.dropdown-item) {
|
||||||
color: currentColor;
|
color: currentColor;
|
||||||
text-decoration: underline; }
|
text-decoration: underline; }
|
||||||
.notification strong {
|
.notification strong {
|
||||||
@@ -3205,7 +3228,9 @@ a.box:active {
|
|||||||
.table.is-hoverable tbody tr:not(.is-selected):hover {
|
.table.is-hoverable tbody tr:not(.is-selected):hover {
|
||||||
background-color: #242424; }
|
background-color: #242424; }
|
||||||
.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover {
|
.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover {
|
||||||
background-color: whitesmoke; }
|
background-color: #242424; }
|
||||||
|
.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even) {
|
||||||
|
background-color: whitesmoke; }
|
||||||
.table.is-narrow td,
|
.table.is-narrow td,
|
||||||
.table.is-narrow th {
|
.table.is-narrow th {
|
||||||
padding: 0.25em 0.5em; }
|
padding: 0.25em 0.5em; }
|
||||||
@@ -3502,7 +3527,7 @@ a.tag:hover {
|
|||||||
position: relative; }
|
position: relative; }
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
background-color: none;
|
background-color: transparent;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1);
|
box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1);
|
||||||
display: flex; }
|
display: flex; }
|
||||||
@@ -3529,11 +3554,11 @@ a.tag:hover {
|
|||||||
position: relative; }
|
position: relative; }
|
||||||
|
|
||||||
.card-content {
|
.card-content {
|
||||||
background-color: none;
|
background-color: transparent;
|
||||||
padding: 1.5rem; }
|
padding: 1.5rem; }
|
||||||
|
|
||||||
.card-footer {
|
.card-footer {
|
||||||
background-color: none;
|
background-color: transparent;
|
||||||
border-top: 1px solid #dbdbdb;
|
border-top: 1px solid #dbdbdb;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
display: flex; }
|
display: flex; }
|
||||||
@@ -3591,13 +3616,18 @@ a.tag:hover {
|
|||||||
padding: 0.375rem 1rem;
|
padding: 0.375rem 1rem;
|
||||||
position: relative; }
|
position: relative; }
|
||||||
|
|
||||||
a.dropdown-item {
|
a.dropdown-item,
|
||||||
|
button.dropdown-item {
|
||||||
padding-right: 3rem;
|
padding-right: 3rem;
|
||||||
white-space: nowrap; }
|
text-align: left;
|
||||||
a.dropdown-item:hover {
|
white-space: nowrap;
|
||||||
|
width: 100%; }
|
||||||
|
a.dropdown-item:hover,
|
||||||
|
button.dropdown-item:hover {
|
||||||
background-color: #0a0a0a;
|
background-color: #0a0a0a;
|
||||||
color: #0a0a0a; }
|
color: #0a0a0a; }
|
||||||
a.dropdown-item.is-active {
|
a.dropdown-item.is-active,
|
||||||
|
button.dropdown-item.is-active {
|
||||||
background-color: #00d1b2;
|
background-color: #00d1b2;
|
||||||
color: #fff; }
|
color: #fff; }
|
||||||
|
|
||||||
@@ -3623,12 +3653,11 @@ a.dropdown-item {
|
|||||||
display: flex; }
|
display: flex; }
|
||||||
.level.is-mobile .level-left + .level-right {
|
.level.is-mobile .level-left + .level-right {
|
||||||
margin-top: 0; }
|
margin-top: 0; }
|
||||||
.level.is-mobile .level-item {
|
.level.is-mobile .level-item:not(:last-child) {
|
||||||
|
margin-bottom: 0;
|
||||||
margin-right: 0.75rem; }
|
margin-right: 0.75rem; }
|
||||||
.level.is-mobile .level-item:not(:last-child) {
|
.level.is-mobile .level-item:not(.is-narrow) {
|
||||||
margin-bottom: 0; }
|
flex-grow: 1; }
|
||||||
.level.is-mobile .level-item:not(.is-narrow) {
|
|
||||||
flex-grow: 1; }
|
|
||||||
@media screen and (min-width: 769px), print {
|
@media screen and (min-width: 769px), print {
|
||||||
.level {
|
.level {
|
||||||
display: flex; }
|
display: flex; }
|
||||||
@@ -3679,6 +3708,32 @@ a.dropdown-item {
|
|||||||
.level-right {
|
.level-right {
|
||||||
display: flex; } }
|
display: flex; } }
|
||||||
|
|
||||||
|
.list {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); }
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
display: block;
|
||||||
|
padding: 0.5em 1em; }
|
||||||
|
.list-item:not(a) {
|
||||||
|
color: whitesmoke; }
|
||||||
|
.list-item:first-child {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px; }
|
||||||
|
.list-item:last-child {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px; }
|
||||||
|
.list-item:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #dbdbdb; }
|
||||||
|
.list-item.is-active {
|
||||||
|
background-color: #00d1b2;
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
a.list-item {
|
||||||
|
background-color: #0a0a0a;
|
||||||
|
cursor: pointer; }
|
||||||
|
|
||||||
.media {
|
.media {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -3722,6 +3777,10 @@ a.dropdown-item {
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
text-align: left; }
|
text-align: left; }
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.media-content {
|
||||||
|
overflow-x: auto; } }
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
font-size: 1rem; }
|
font-size: 1rem; }
|
||||||
.menu.is-small {
|
.menu.is-small {
|
||||||
@@ -3891,6 +3950,7 @@ a.dropdown-item {
|
|||||||
.modal {
|
.modal {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: none;
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -3927,7 +3987,8 @@ a.dropdown-item {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-height: calc(100vh - 40px);
|
max-height: calc(100vh - 40px);
|
||||||
overflow: hidden; }
|
overflow: hidden;
|
||||||
|
-ms-overflow-y: visible; }
|
||||||
|
|
||||||
.modal-card-head,
|
.modal-card-head,
|
||||||
.modal-card-foot {
|
.modal-card-foot {
|
||||||
@@ -3984,6 +4045,8 @@ a.dropdown-item {
|
|||||||
color: #0a0a0a; }
|
color: #0a0a0a; }
|
||||||
.navbar.is-white .navbar-brand .navbar-link::after {
|
.navbar.is-white .navbar-brand .navbar-link::after {
|
||||||
border-color: #0a0a0a; }
|
border-color: #0a0a0a; }
|
||||||
|
.navbar.is-white .navbar-burger {
|
||||||
|
color: #0a0a0a; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-white .navbar-start > .navbar-item,
|
.navbar.is-white .navbar-start > .navbar-item,
|
||||||
.navbar.is-white .navbar-start .navbar-link,
|
.navbar.is-white .navbar-start .navbar-link,
|
||||||
@@ -4022,6 +4085,8 @@ a.dropdown-item {
|
|||||||
color: white; }
|
color: white; }
|
||||||
.navbar.is-black .navbar-brand .navbar-link::after {
|
.navbar.is-black .navbar-brand .navbar-link::after {
|
||||||
border-color: white; }
|
border-color: white; }
|
||||||
|
.navbar.is-black .navbar-burger {
|
||||||
|
color: white; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-black .navbar-start > .navbar-item,
|
.navbar.is-black .navbar-start > .navbar-item,
|
||||||
.navbar.is-black .navbar-start .navbar-link,
|
.navbar.is-black .navbar-start .navbar-link,
|
||||||
@@ -4060,6 +4125,8 @@ a.dropdown-item {
|
|||||||
color: #363636; }
|
color: #363636; }
|
||||||
.navbar.is-light .navbar-brand .navbar-link::after {
|
.navbar.is-light .navbar-brand .navbar-link::after {
|
||||||
border-color: #363636; }
|
border-color: #363636; }
|
||||||
|
.navbar.is-light .navbar-burger {
|
||||||
|
color: #363636; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-light .navbar-start > .navbar-item,
|
.navbar.is-light .navbar-start > .navbar-item,
|
||||||
.navbar.is-light .navbar-start .navbar-link,
|
.navbar.is-light .navbar-start .navbar-link,
|
||||||
@@ -4098,6 +4165,8 @@ a.dropdown-item {
|
|||||||
color: whitesmoke; }
|
color: whitesmoke; }
|
||||||
.navbar.is-dark .navbar-brand .navbar-link::after {
|
.navbar.is-dark .navbar-brand .navbar-link::after {
|
||||||
border-color: whitesmoke; }
|
border-color: whitesmoke; }
|
||||||
|
.navbar.is-dark .navbar-burger {
|
||||||
|
color: whitesmoke; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-dark .navbar-start > .navbar-item,
|
.navbar.is-dark .navbar-start > .navbar-item,
|
||||||
.navbar.is-dark .navbar-start .navbar-link,
|
.navbar.is-dark .navbar-start .navbar-link,
|
||||||
@@ -4136,6 +4205,8 @@ a.dropdown-item {
|
|||||||
color: #fff; }
|
color: #fff; }
|
||||||
.navbar.is-primary .navbar-brand .navbar-link::after {
|
.navbar.is-primary .navbar-brand .navbar-link::after {
|
||||||
border-color: #fff; }
|
border-color: #fff; }
|
||||||
|
.navbar.is-primary .navbar-burger {
|
||||||
|
color: #fff; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-primary .navbar-start > .navbar-item,
|
.navbar.is-primary .navbar-start > .navbar-item,
|
||||||
.navbar.is-primary .navbar-start .navbar-link,
|
.navbar.is-primary .navbar-start .navbar-link,
|
||||||
@@ -4174,6 +4245,8 @@ a.dropdown-item {
|
|||||||
color: #fff; }
|
color: #fff; }
|
||||||
.navbar.is-link .navbar-brand .navbar-link::after {
|
.navbar.is-link .navbar-brand .navbar-link::after {
|
||||||
border-color: #fff; }
|
border-color: #fff; }
|
||||||
|
.navbar.is-link .navbar-burger {
|
||||||
|
color: #fff; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-link .navbar-start > .navbar-item,
|
.navbar.is-link .navbar-start > .navbar-item,
|
||||||
.navbar.is-link .navbar-start .navbar-link,
|
.navbar.is-link .navbar-start .navbar-link,
|
||||||
@@ -4212,6 +4285,8 @@ a.dropdown-item {
|
|||||||
color: #fff; }
|
color: #fff; }
|
||||||
.navbar.is-info .navbar-brand .navbar-link::after {
|
.navbar.is-info .navbar-brand .navbar-link::after {
|
||||||
border-color: #fff; }
|
border-color: #fff; }
|
||||||
|
.navbar.is-info .navbar-burger {
|
||||||
|
color: #fff; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-info .navbar-start > .navbar-item,
|
.navbar.is-info .navbar-start > .navbar-item,
|
||||||
.navbar.is-info .navbar-start .navbar-link,
|
.navbar.is-info .navbar-start .navbar-link,
|
||||||
@@ -4250,6 +4325,8 @@ a.dropdown-item {
|
|||||||
color: #fff; }
|
color: #fff; }
|
||||||
.navbar.is-success .navbar-brand .navbar-link::after {
|
.navbar.is-success .navbar-brand .navbar-link::after {
|
||||||
border-color: #fff; }
|
border-color: #fff; }
|
||||||
|
.navbar.is-success .navbar-burger {
|
||||||
|
color: #fff; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-success .navbar-start > .navbar-item,
|
.navbar.is-success .navbar-start > .navbar-item,
|
||||||
.navbar.is-success .navbar-start .navbar-link,
|
.navbar.is-success .navbar-start .navbar-link,
|
||||||
@@ -4288,6 +4365,8 @@ a.dropdown-item {
|
|||||||
color: rgba(0, 0, 0, 0.7); }
|
color: rgba(0, 0, 0, 0.7); }
|
||||||
.navbar.is-warning .navbar-brand .navbar-link::after {
|
.navbar.is-warning .navbar-brand .navbar-link::after {
|
||||||
border-color: rgba(0, 0, 0, 0.7); }
|
border-color: rgba(0, 0, 0, 0.7); }
|
||||||
|
.navbar.is-warning .navbar-burger {
|
||||||
|
color: rgba(0, 0, 0, 0.7); }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-warning .navbar-start > .navbar-item,
|
.navbar.is-warning .navbar-start > .navbar-item,
|
||||||
.navbar.is-warning .navbar-start .navbar-link,
|
.navbar.is-warning .navbar-start .navbar-link,
|
||||||
@@ -4326,6 +4405,8 @@ a.dropdown-item {
|
|||||||
color: #fff; }
|
color: #fff; }
|
||||||
.navbar.is-danger .navbar-brand .navbar-link::after {
|
.navbar.is-danger .navbar-brand .navbar-link::after {
|
||||||
border-color: #fff; }
|
border-color: #fff; }
|
||||||
|
.navbar.is-danger .navbar-burger {
|
||||||
|
color: #fff; }
|
||||||
@media screen and (min-width: 1088px) {
|
@media screen and (min-width: 1088px) {
|
||||||
.navbar.is-danger .navbar-start > .navbar-item,
|
.navbar.is-danger .navbar-start > .navbar-item,
|
||||||
.navbar.is-danger .navbar-start .navbar-link,
|
.navbar.is-danger .navbar-start .navbar-link,
|
||||||
@@ -4395,6 +4476,7 @@ body.has-navbar-fixed-bottom {
|
|||||||
overflow-y: hidden; }
|
overflow-y: hidden; }
|
||||||
|
|
||||||
.navbar-burger {
|
.navbar-burger {
|
||||||
|
color: #4a4a4a;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
height: 3.25rem;
|
height: 3.25rem;
|
||||||
@@ -4481,9 +4563,9 @@ a.navbar-item,
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 1; }
|
flex-shrink: 1; }
|
||||||
|
|
||||||
.navbar-link {
|
.navbar-link:not(.is-arrowless) {
|
||||||
padding-right: 2.5em; }
|
padding-right: 2.5em; }
|
||||||
.navbar-link::after {
|
.navbar-link:not(.is-arrowless)::after {
|
||||||
border-color: #00d1b2;
|
border-color: #00d1b2;
|
||||||
margin-top: -0.375em;
|
margin-top: -0.375em;
|
||||||
right: 1.125em; }
|
right: 1.125em; }
|
||||||
@@ -4645,10 +4727,10 @@ a.navbar-item,
|
|||||||
display: block; }
|
display: block; }
|
||||||
.navbar > .container .navbar-brand,
|
.navbar > .container .navbar-brand,
|
||||||
.container > .navbar .navbar-brand {
|
.container > .navbar .navbar-brand {
|
||||||
margin-left: -1rem; }
|
margin-left: -.75rem; }
|
||||||
.navbar > .container .navbar-menu,
|
.navbar > .container .navbar-menu,
|
||||||
.container > .navbar .navbar-menu {
|
.container > .navbar .navbar-menu {
|
||||||
margin-right: -1rem; }
|
margin-right: -.75rem; }
|
||||||
.navbar.is-fixed-bottom-desktop, .navbar.is-fixed-top-desktop {
|
.navbar.is-fixed-bottom-desktop, .navbar.is-fixed-top-desktop {
|
||||||
left: 0;
|
left: 0;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -5816,22 +5898,265 @@ label.panel-block {
|
|||||||
padding-right: var(--columnGap); }
|
padding-right: var(--columnGap); }
|
||||||
.columns.is-variable.is-0 {
|
.columns.is-variable.is-0 {
|
||||||
--columnGap: 0rem; }
|
--columnGap: 0rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-0-mobile {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-0-tablet {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-0-tablet-only {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-0-touch {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-0-desktop {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-0-desktop-only {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-0-widescreen {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-0-widescreen-only {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-0-fullhd {
|
||||||
|
--columnGap: 0rem; } }
|
||||||
.columns.is-variable.is-1 {
|
.columns.is-variable.is-1 {
|
||||||
--columnGap: 0.25rem; }
|
--columnGap: 0.25rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-1-mobile {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-1-tablet {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-1-tablet-only {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-1-touch {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-1-desktop {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-1-desktop-only {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-1-widescreen {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-1-widescreen-only {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-1-fullhd {
|
||||||
|
--columnGap: 0.25rem; } }
|
||||||
.columns.is-variable.is-2 {
|
.columns.is-variable.is-2 {
|
||||||
--columnGap: 0.5rem; }
|
--columnGap: 0.5rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-2-mobile {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-2-tablet {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-2-tablet-only {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-2-touch {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-2-desktop {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-2-desktop-only {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-2-widescreen {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-2-widescreen-only {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-2-fullhd {
|
||||||
|
--columnGap: 0.5rem; } }
|
||||||
.columns.is-variable.is-3 {
|
.columns.is-variable.is-3 {
|
||||||
--columnGap: 0.75rem; }
|
--columnGap: 0.75rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-3-mobile {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-3-tablet {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-3-tablet-only {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-3-touch {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-3-desktop {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-3-desktop-only {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-3-widescreen {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-3-widescreen-only {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-3-fullhd {
|
||||||
|
--columnGap: 0.75rem; } }
|
||||||
.columns.is-variable.is-4 {
|
.columns.is-variable.is-4 {
|
||||||
--columnGap: 1rem; }
|
--columnGap: 1rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-4-mobile {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-4-tablet {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-4-tablet-only {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-4-touch {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-4-desktop {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-4-desktop-only {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-4-widescreen {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-4-widescreen-only {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-4-fullhd {
|
||||||
|
--columnGap: 1rem; } }
|
||||||
.columns.is-variable.is-5 {
|
.columns.is-variable.is-5 {
|
||||||
--columnGap: 1.25rem; }
|
--columnGap: 1.25rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-5-mobile {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-5-tablet {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-5-tablet-only {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-5-touch {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-5-desktop {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-5-desktop-only {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-5-widescreen {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-5-widescreen-only {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-5-fullhd {
|
||||||
|
--columnGap: 1.25rem; } }
|
||||||
.columns.is-variable.is-6 {
|
.columns.is-variable.is-6 {
|
||||||
--columnGap: 1.5rem; }
|
--columnGap: 1.5rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-6-mobile {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-6-tablet {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-6-tablet-only {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-6-touch {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-6-desktop {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-6-desktop-only {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-6-widescreen {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-6-widescreen-only {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-6-fullhd {
|
||||||
|
--columnGap: 1.5rem; } }
|
||||||
.columns.is-variable.is-7 {
|
.columns.is-variable.is-7 {
|
||||||
--columnGap: 1.75rem; }
|
--columnGap: 1.75rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-7-mobile {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-7-tablet {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-7-tablet-only {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-7-touch {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-7-desktop {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-7-desktop-only {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-7-widescreen {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-7-widescreen-only {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-7-fullhd {
|
||||||
|
--columnGap: 1.75rem; } }
|
||||||
.columns.is-variable.is-8 {
|
.columns.is-variable.is-8 {
|
||||||
--columnGap: 2rem; }
|
--columnGap: 2rem; }
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.columns.is-variable.is-8-mobile {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (min-width: 769px), print {
|
||||||
|
.columns.is-variable.is-8-tablet {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (min-width: 769px) and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-8-tablet-only {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (max-width: 1087px) {
|
||||||
|
.columns.is-variable.is-8-touch {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (min-width: 1088px) {
|
||||||
|
.columns.is-variable.is-8-desktop {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (min-width: 1088px) and (max-width: 1279px) {
|
||||||
|
.columns.is-variable.is-8-desktop-only {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.columns.is-variable.is-8-widescreen {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (min-width: 1280px) and (max-width: 1471px) {
|
||||||
|
.columns.is-variable.is-8-widescreen-only {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
@media screen and (min-width: 1472px) {
|
||||||
|
.columns.is-variable.is-8-fullhd {
|
||||||
|
--columnGap: 2rem; } }
|
||||||
|
|
||||||
.tile {
|
.tile {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
@@ -6356,16 +6681,18 @@ label.panel-block {
|
|||||||
.hero.is-large .hero-body {
|
.hero.is-large .hero-body {
|
||||||
padding-bottom: 18rem;
|
padding-bottom: 18rem;
|
||||||
padding-top: 18rem; } }
|
padding-top: 18rem; } }
|
||||||
.hero.is-halfheight .hero-body, .hero.is-fullheight .hero-body {
|
.hero.is-halfheight .hero-body, .hero.is-fullheight .hero-body, .hero.is-fullheight-with-navbar .hero-body {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex; }
|
display: flex; }
|
||||||
.hero.is-halfheight .hero-body > .container, .hero.is-fullheight .hero-body > .container {
|
.hero.is-halfheight .hero-body > .container, .hero.is-fullheight .hero-body > .container, .hero.is-fullheight-with-navbar .hero-body > .container {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 1; }
|
flex-shrink: 1; }
|
||||||
.hero.is-halfheight {
|
.hero.is-halfheight {
|
||||||
min-height: 50vh; }
|
min-height: 50vh; }
|
||||||
.hero.is-fullheight {
|
.hero.is-fullheight {
|
||||||
min-height: 100vh; }
|
min-height: 100vh; }
|
||||||
|
.hero.is-fullheight-with-navbar {
|
||||||
|
min-height: calc(100vh - 3.25rem); }
|
||||||
|
|
||||||
.hero-video {
|
.hero-video {
|
||||||
overflow: hidden; }
|
overflow: hidden; }
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
; This is the service that broadcast market data to the
|
||||||
|
; world. The users need to expect websocket and REST api.
|
||||||
|
|
||||||
|
|
||||||
(ns skirmish.bookkeeper.core
|
(ns skirmish.bookkeeper.core
|
||||||
(:require [org.httpkit.server :refer [run-server]]
|
(:require [org.httpkit.server :refer [run-server]]
|
||||||
[compojure.handler :refer [api]]
|
[compojure.handler :refer [api]]
|
||||||
|
|||||||
@@ -4,15 +4,50 @@
|
|||||||
[datomic.api :as d]
|
[datomic.api :as d]
|
||||||
[slingshot.slingshot :refer [try+ throw+]]
|
[slingshot.slingshot :refer [try+ throw+]]
|
||||||
[skirmish.rest.util :refer [build-response not-found-resp get-body]]
|
[skirmish.rest.util :refer [build-response not-found-resp get-body]]
|
||||||
[taoensso.timbre :as timbre]))
|
[taoensso.timbre :as timbre]
|
||||||
|
[skirmish.routes.core :as routes]
|
||||||
|
[clj-http.client :as http-client]))
|
||||||
|
|
||||||
|
|
||||||
(defn create-user [conn]
|
(defn generate-amount-for-new-user [seed]
|
||||||
|
(let [random (new java.util.Random seed)
|
||||||
|
random-fn #(. random nextInt 20000)]
|
||||||
|
(* 100 (reduce + (take 100 (repeatedly random-fn))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-account [account-id sec-id]
|
||||||
|
#:skirmish.broker.account{:id account-id
|
||||||
|
:sec-id sec-id
|
||||||
|
:private-key (str account-id)})
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-user-account [seed default-sec-id]
|
||||||
|
(let [url (routes/get-path-for :ledger/security {:sec-id (name default-sec-id)})
|
||||||
|
account-id (java.util.UUID/randomUUID)
|
||||||
|
amount (generate-amount-for-new-user seed)
|
||||||
|
payload [amount (str account-id)]]
|
||||||
|
(timbre/info "Creating new account " account-id " with ammount " amount " of " default-sec-id)
|
||||||
|
(timbre/info url)
|
||||||
|
(http-client/post url {:body (str payload)})
|
||||||
|
(create-account account-id default-sec-id)))
|
||||||
|
|
||||||
|
(defn create-user-seed-from-email [email]
|
||||||
|
(let [hasher (java.security.MessageDigest/getInstance "SHA-256")
|
||||||
|
salt (. "this should be private and set in config" getBytes "utf-8")
|
||||||
|
msg (. email getBytes "utf-8")
|
||||||
|
_ (doto hasher (.update salt) (.update msg))
|
||||||
|
hash (. hasher digest)]
|
||||||
|
(int (mod (new BigInteger 1 hash) (biginteger Integer/MAX_VALUE)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-user [conn default-sec-id]
|
||||||
(fn [{body :body :as request}]
|
(fn [{body :body :as request}]
|
||||||
(try+
|
(try+
|
||||||
(let [email (-> body get-body :email)]
|
(let [email (-> body get-body :email)
|
||||||
(timbre/info "Creating new user" email)
|
_ (timbre/info "Creating new user" email)
|
||||||
@(d/transact conn [[:skirmish.broker.user/create-user email]])
|
seed (create-user-seed-from-email email)
|
||||||
|
account (create-user-account seed default-sec-id)]
|
||||||
|
@(d/transact conn [[:skirmish.broker.user/create-user email [account]]])
|
||||||
(build-response request {:status 201 :headers {"location" (str "/api/1/users/" email)}}))
|
(build-response request {:status 201 :headers {"location" (str "/api/1/users/" email)}}))
|
||||||
(catch [:error-type :4XX] e (build-response request e))
|
(catch [:error-type :4XX] e (build-response request e))
|
||||||
(catch [:error-type :5XX] e (build-response request e)))))
|
(catch [:error-type :5XX] e (build-response request e)))))
|
||||||
@@ -24,6 +59,7 @@
|
|||||||
ret (map #(assoc % :skirmish.broker.account/url (url %)) accounts)]
|
ret (map #(assoc % :skirmish.broker.account/url (url %)) accounts)]
|
||||||
(assoc user :skirmish.broker.user/accounts ret)))
|
(assoc user :skirmish.broker.user/accounts ret)))
|
||||||
|
|
||||||
|
|
||||||
(defn get-user [conn]
|
(defn get-user [conn]
|
||||||
(fn [{{email :email} :route-params :as request}]
|
(fn [{{email :email} :route-params :as request}]
|
||||||
(try+
|
(try+
|
||||||
@@ -54,14 +90,15 @@
|
|||||||
(catch [:error-type :4XX] e (build-response request e))
|
(catch [:error-type :4XX] e (build-response request e))
|
||||||
(catch [:error-type :5XX] e (build-response request e)))))
|
(catch [:error-type :5XX] e (build-response request e)))))
|
||||||
|
|
||||||
|
|
||||||
(defn get-account [conn]
|
(defn get-account [conn]
|
||||||
(fn [email account]))
|
(fn [email account]))
|
||||||
|
|
||||||
|
|
||||||
(defn create-routes [conn]
|
(defn create-routes [conn config]
|
||||||
(routes
|
(routes
|
||||||
(context "/api/1" []
|
(context "/api/1" []
|
||||||
(POST "/users" [] (create-user conn))
|
(POST "/users" [] (create-user conn (:default-sec-id config)))
|
||||||
(GET "/users" [] (get-all-users conn))
|
(GET "/users" [] (get-all-users conn))
|
||||||
(GET "/users/:email" [email] (get-user conn))
|
(GET "/users/:email" [email] (get-user conn))
|
||||||
(GET "/users/:email/accounts/:account" [email account] (get-account conn)))
|
(GET "/users/:email/accounts/:account" [email account] (get-account conn)))
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
; The backend of the skirmish web user interface.
|
||||||
|
|
||||||
(ns skirmish.broker.core
|
(ns skirmish.broker.core
|
||||||
(:require [org.httpkit.server :refer [run-server]]
|
(:require [org.httpkit.server :refer [run-server]]
|
||||||
[compojure.handler :refer [api]]
|
[compojure.handler :refer [api]]
|
||||||
@@ -11,7 +13,7 @@
|
|||||||
(defn -main [config securities-api exchange-apis]
|
(defn -main [config securities-api exchange-apis]
|
||||||
(let [webserver-config (:api-broker config)
|
(let [webserver-config (:api-broker config)
|
||||||
conn (create-connection (-> webserver-config :datomic :transactor-url))
|
conn (create-connection (-> webserver-config :datomic :transactor-url))
|
||||||
api-routes (create-routes conn)]
|
api-routes (create-routes conn webserver-config)]
|
||||||
(timbre/info "Running skirmish.broker.core server with config: " webserver-config)
|
(timbre/info "Running skirmish.broker.core server with config: " webserver-config)
|
||||||
(run-server
|
(run-server
|
||||||
(-> api-routes api cors-middleware)
|
(-> api-routes api cors-middleware)
|
||||||
|
|||||||
@@ -8,12 +8,11 @@
|
|||||||
:db/doc "Create a new user in the system."
|
:db/doc "Create a new user in the system."
|
||||||
:db/fn (d/function {:lang "clojure"
|
:db/fn (d/function {:lang "clojure"
|
||||||
:requires ['[slingshot.slingshot]]
|
:requires ['[slingshot.slingshot]]
|
||||||
:params '[db email]
|
:params '[db email accounts]
|
||||||
:code '(let [existing-user (d/pull db '[:db/id] [:skirmish.broker.user/email email])]
|
:code '(let [existing-user (d/pull db '[:db/id] [:skirmish.broker.user/email email])]
|
||||||
;:skirmish.broker.user/accounts []])]
|
|
||||||
(when (-> existing-user :db/id some?)
|
(when (-> existing-user :db/id some?)
|
||||||
(slingshot.slingshot/throw+ {:error-type :4XX :status 400 :body {:error "A user with this email already exists."}}))
|
(slingshot.slingshot/throw+ {:error-type :4XX :status 400 :body {:error "A user with this email already exists."}}))
|
||||||
[#:skirmish.broker.user{:email email}])})}])
|
[#:skirmish.broker.user{:email email :accounts accounts}])})}])
|
||||||
|
|
||||||
|
|
||||||
(def accounts-schema [
|
(def accounts-schema [
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
(ns skirmish.cli.benchmark
|
|
||||||
(:require [clojure.math.combinatorics :refer [cartesian-product]]
|
|
||||||
[clojure.core.async :refer [go chan <! >! close! to-chan close!] :as async]
|
|
||||||
[gniazdo.core :as ws]
|
|
||||||
[clj-http.client :as client]
|
|
||||||
[com.stuartsierra.frequencies :as freq]
|
|
||||||
[clojure.edn :as edn]))
|
|
||||||
|
|
||||||
|
|
||||||
(def ids (atom 0))
|
|
||||||
|
|
||||||
|
|
||||||
(defn web-socket-to-channel [url output-ch]
|
|
||||||
(let [on-receive #(go (>! output-ch %))
|
|
||||||
headers {"accept" "application/edn"}]
|
|
||||||
(ws/connect url :headers headers :on-receive on-receive)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn create-orders-channel [securities]
|
|
||||||
(let [output (chan 1 (map edn/read-string))
|
|
||||||
url #(format "ws://localhost:%s/api/1/securities/%s/orders" 8080 %)
|
|
||||||
xf (comp (map :sec-id)
|
|
||||||
(map name)
|
|
||||||
(map url)
|
|
||||||
(map #(web-socket-to-channel % output)))]
|
|
||||||
(doall (sequence xf securities))
|
|
||||||
(async/pub output :book.order/id)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn create-requests-payload-for-security [i security id]
|
|
||||||
(let [rd (new java.util.Random i)
|
|
||||||
rand-bool (= (mod (. rd nextInt) 2) 0) ;; java random number generator REALLY sucks
|
|
||||||
type (if rand-bool :sell :buy)
|
|
||||||
price (+ (* 3 (. rd nextGaussian)) 30)
|
|
||||||
int-price (int (* price 100))
|
|
||||||
quantity (reduce + 1 (map #(. rd nextInt (inc %)) (range 100)))]
|
|
||||||
|
|
||||||
{:type type :quantity quantity :price int-price :security security :benchmark/id id}))
|
|
||||||
|
|
||||||
|
|
||||||
(defn create-requests-for-security [[i security]]
|
|
||||||
(let [req-id (swap! ids inc)
|
|
||||||
payload (create-requests-payload-for-security i (str (:sec-id security)) req-id)
|
|
||||||
sec-id-str (-> security :sec-id name)
|
|
||||||
params {:throw-exceptions false :content-type :edn :headers {"accept" "application/edn"}}
|
|
||||||
url (format "http://localhost:%s/api/1/securities/%s" 8080 sec-id-str)]
|
|
||||||
(fn []
|
|
||||||
(let [now (System/nanoTime)]
|
|
||||||
(client/post url (assoc params :form-params (assoc payload :benchmark/request-time now)))))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn make-requests [n securities]
|
|
||||||
(map create-requests-for-security (cartesian-product (range n) securities)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn no-more-then-n-simultaneously [f input-chan n]
|
|
||||||
(let [buffer (chan)]
|
|
||||||
(go (async/pipeline n buffer (map f) input-chan))
|
|
||||||
buffer))
|
|
||||||
|
|
||||||
(defn decode-response [response]
|
|
||||||
(let [now (System/nanoTime)
|
|
||||||
ret {}
|
|
||||||
ret (assoc ret :status (:status response))
|
|
||||||
ret (assoc ret :order (edn/read-string (:body response)))
|
|
||||||
ret (assoc ret :duration (/ (- now (get-in ret [:order :benchmark/request-time])) (Math/pow 10 9)))]
|
|
||||||
ret))
|
|
||||||
|
|
||||||
|
|
||||||
(defn benchmark [requests orders]
|
|
||||||
(let [decoded-responses (chan 1 (map decode-response))
|
|
||||||
responses (no-more-then-n-simultaneously #(%) (to-chan requests) 10)]
|
|
||||||
(go (async/pipe responses decoded-responses))
|
|
||||||
decoded-responses))
|
|
||||||
|
|
||||||
|
|
||||||
(defn compute-result [results new-result]
|
|
||||||
(let [status-code (:status new-result)
|
|
||||||
duration (:duration new-result)
|
|
||||||
old-status-code-count (get-in results [:status status-code] 0)
|
|
||||||
ret results
|
|
||||||
ret (assoc-in ret [:status status-code] (inc old-status-code-count))
|
|
||||||
ret (assoc ret :durations (conj (:durations ret) duration))]
|
|
||||||
ret))
|
|
||||||
|
|
||||||
(defn pp-durations [durations]
|
|
||||||
(let [freq-map (frequencies durations)]
|
|
||||||
(print (freq/stats freq-map))))
|
|
||||||
|
|
||||||
(defn display-results [results]
|
|
||||||
(println results)
|
|
||||||
(println "number of request: " (:n results))
|
|
||||||
(println "status map: " (:status results))
|
|
||||||
(pp-durations (:durations results)))
|
|
||||||
|
|
||||||
(defn -main [config exchange-apis]
|
|
||||||
(let [benchmark-config (:benchmark config)
|
|
||||||
requests (-> benchmark-config :number-of-requests-per-securities (make-requests (:securities config)))
|
|
||||||
orders (create-orders-channel (:securities config))
|
|
||||||
results (benchmark requests orders)]
|
|
||||||
(display-results (async/<!! (async/reduce compute-result {} results)))))
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
(ns skirmish.cli.core)
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
(ns skirmish.cli.exchange
|
|
||||||
(:require [skirmish.exchange.match :refer [sell?]]
|
|
||||||
[skirmish.util :refer :all]
|
|
||||||
[skirmish.exchange.exchange :refer [empty-book create-exchange-for-security]]
|
|
||||||
[skirmish.order] ;; for spec side effect
|
|
||||||
[slingshot.slingshot :refer [try+ throw+]]
|
|
||||||
[clojure.edn :as edn]
|
|
||||||
[clojure.core.async :refer [chan go >!! <!!]]
|
|
||||||
[clojure.spec.alpha :as s]))
|
|
||||||
|
|
||||||
|
|
||||||
(def incoming-order-channel (chan))
|
|
||||||
|
|
||||||
;(def trades-channel (chan))
|
|
||||||
;
|
|
||||||
;(def new-book-order-channel (chan))
|
|
||||||
|
|
||||||
(defn print-chan [prefix c]
|
|
||||||
(loop [shit (<!! c)]
|
|
||||||
(println prefix shit)
|
|
||||||
(recur (<!! c))))
|
|
||||||
|
|
||||||
;(def master-book {:sell (create-sell-book)
|
|
||||||
; :buy (create-buy-book)})
|
|
||||||
|
|
||||||
;;; repl things.
|
|
||||||
(defn read-order []
|
|
||||||
(try
|
|
||||||
(print "$ ")
|
|
||||||
(let [order (edn/read-string (read-line))
|
|
||||||
valid? (s/valid? :book.order/order order)]
|
|
||||||
(if valid? order nil))
|
|
||||||
(catch Exception e nil)))
|
|
||||||
|
|
||||||
(def order-ids (atom -1))
|
|
||||||
|
|
||||||
|
|
||||||
(defn add-id-to-order [order]
|
|
||||||
(assoc order :book.order/id (swap! order-ids inc)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn -main [& args]
|
|
||||||
(let [initial-book empty-book
|
|
||||||
incoming-order-channel (chan)
|
|
||||||
[trades-channel new-book-order-channel] (create-exchange-for-security initial-book incoming-order-channel)]
|
|
||||||
(go (print-chan "trade > " trades-channel))
|
|
||||||
(go (print-chan "order > " new-book-order-channel))
|
|
||||||
(loop [order (read-order)]
|
|
||||||
(if (some? order)
|
|
||||||
(do
|
|
||||||
(let [order-with-id (add-id-to-order order)]
|
|
||||||
(>!! incoming-order-channel order-with-id)
|
|
||||||
(println "\n\norder accepted with id: " (:book.order/id order-with-id))))
|
|
||||||
|
|
||||||
(println "invalid order"))
|
|
||||||
(recur (read-order)))))
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
(ns skirmish.core
|
(ns skirmish.core
|
||||||
(:require [skirmish.cli.core]
|
(:require [skirmish.exchange.core]
|
||||||
[skirmish.cli.benchmark]
|
|
||||||
[skirmish.exchange.core]
|
|
||||||
[skirmish.wui.server]
|
[skirmish.wui.server]
|
||||||
[skirmish.bookkeeper.core]
|
[skirmish.bookkeeper.core]
|
||||||
[skirmish.exchange-gateway.core]
|
[skirmish.exchange-gateway.core]
|
||||||
|
|||||||
@@ -106,6 +106,3 @@
|
|||||||
(<! orders-copy)
|
(<! orders-copy)
|
||||||
(<! trades-copy)
|
(<! trades-copy)
|
||||||
(recur (<! new-order-publisher) new-book)))))
|
(recur (<! new-order-publisher) new-book)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -153,6 +153,11 @@
|
|||||||
|
|
||||||
|
|
||||||
(defn emit-securities [conn]
|
(defn emit-securities [conn]
|
||||||
|
"Emit securities in the specified wallet. This is the only way to
|
||||||
|
create securities.
|
||||||
|
|
||||||
|
payload
|
||||||
|
[:amount :acount]"
|
||||||
(fn [{{sec-id-str :sec-id} :route-params body :body :as request}]
|
(fn [{{sec-id-str :sec-id} :route-params body :body :as request}]
|
||||||
(try+
|
(try+
|
||||||
(let [emit-request (-> body get-body (get-emit-securities-request (keyword sec-id-str)))]
|
(let [emit-request (-> body get-body (get-emit-securities-request (keyword sec-id-str)))]
|
||||||
@@ -184,7 +189,6 @@
|
|||||||
|
|
||||||
(defn create-routes-for-ledger [conn]
|
(defn create-routes-for-ledger [conn]
|
||||||
(compojure/routes
|
(compojure/routes
|
||||||
;; (routes/get-context :ledger)
|
|
||||||
(routes/context :ledger []
|
(routes/context :ledger []
|
||||||
(routes/GET :ledger/home [] get-api-description)
|
(routes/GET :ledger/home [] get-api-description)
|
||||||
(routes/GET :ledger/accounts [] (get-available-accounts conn))
|
(routes/GET :ledger/accounts [] (get-available-accounts conn))
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
(head)
|
(head)
|
||||||
[:body.body-container
|
[:body.body-container
|
||||||
mount-target
|
mount-target
|
||||||
(include-js "/js/app.js")]))
|
(include-js "/js/app.js")
|
||||||
|
(include-js "https://sidecar.gitter.im/dist/sidecar.v1.js")]))
|
||||||
|
|
||||||
(defn loading-page []
|
(defn loading-page []
|
||||||
(html5
|
(html5
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
(ns skirmish.wui.components.home-page
|
(ns skirmish.wui.components.home-page
|
||||||
(:require [cljs-http.client :as http]
|
(:require [cljs-http.client :as http]
|
||||||
[reagent.core :refer [atom]]
|
[reagent.core :refer [atom]]
|
||||||
|
[skirmish.wui.util :refer [email?]]
|
||||||
[cljs.core.async :refer [<!] :refer-macros [go]]
|
[cljs.core.async :refer [<!] :refer-macros [go]]
|
||||||
[secretary.core :as secretary]))
|
[secretary.core :as secretary]))
|
||||||
|
|
||||||
(defn email? [s]
|
|
||||||
(let [email-pattern #"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"]
|
|
||||||
(->> s str (re-matches email-pattern) some?)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn create-user [email]
|
(defn create-user [email]
|
||||||
(go
|
(go
|
||||||
(let [url "http://localhost:1605/api/1/users"
|
(let [url "http://localhost:1605/api/1/users"
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
(ns skirmish.wui.components.otc-page
|
||||||
|
(:require [reagent.core :refer [atom]]
|
||||||
|
[skirmish.wui.util :refer [email?]]))
|
||||||
|
|
||||||
|
|
||||||
|
(defn set-amount [state event]
|
||||||
|
(let [inserted-value (-> event .-target .-value)
|
||||||
|
new-value (first (re-find #"^\d+(\.\d*)?" inserted-value))]
|
||||||
|
(swap! state #(assoc % :value new-value))))
|
||||||
|
|
||||||
|
(defn set-email [state event]
|
||||||
|
(let [new-email (-> event .-target .-value)]
|
||||||
|
(swap! state #(assoc % :email new-email))))
|
||||||
|
|
||||||
|
(defn set-sec-id [state event]
|
||||||
|
(let [sec-id (-> event .-target .-value)]
|
||||||
|
(swap! state #(assoc % :sec-id sec-id))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn ready-to-transfer? [state]
|
||||||
|
(and (-> state :value js/parseInt pos?)
|
||||||
|
(-> state :email email?)))
|
||||||
|
|
||||||
|
(defn direct-send-confirmation []
|
||||||
|
[:div.model
|
||||||
|
[:div.modal-background]
|
||||||
|
[:div.modal-content
|
||||||
|
[:div.modal-card
|
||||||
|
[:header.modal-card-head
|
||||||
|
[:p.modal-card-title "Modal title"]
|
||||||
|
[:button.delete]]]]
|
||||||
|
[:a.button.modal-close.is-large {:aria-label "close"} "dslkds"]])
|
||||||
|
|
||||||
|
(defn direct-send []
|
||||||
|
(let [state (atom {})]
|
||||||
|
(fn []
|
||||||
|
[:div
|
||||||
|
[:div.content
|
||||||
|
[:p "Send securities to another person."]
|
||||||
|
[:pre (str @state)]]
|
||||||
|
[:form.form
|
||||||
|
[:div.field.has-addons.has-addons-centered
|
||||||
|
[:div.control
|
||||||
|
[:span.select
|
||||||
|
[:select {:value (:sec-id @state)
|
||||||
|
:on-change (partial set-sec-id state)}
|
||||||
|
[:option {:value :foo :selected true} "damp"]
|
||||||
|
[:option {:value :bar } "blah blah blah"]]]]
|
||||||
|
[:div.control
|
||||||
|
[:input.input {:type "text"
|
||||||
|
:value (:value @state)
|
||||||
|
:on-change (partial set-amount state)
|
||||||
|
:placeholder "110.90"}]]
|
||||||
|
[:div.control
|
||||||
|
[:input.input {:type "text"
|
||||||
|
:value (:email @state)
|
||||||
|
:on-change (partial set-email state)
|
||||||
|
:placeholder "boris.johnson@redneck.co.uk"}]]
|
||||||
|
[:a.button.is-primary {:style {:marginLeft "1em"}
|
||||||
|
:disabled (-> @state ready-to-transfer? not)} "transfer"]]]])))
|
||||||
|
|
||||||
|
(defn page [state]
|
||||||
|
[:div
|
||||||
|
;[direct-send-confirmation]
|
||||||
|
[:div.content
|
||||||
|
[:h5 "Do transaction without passing by the exchange."]]
|
||||||
|
|
||||||
|
[direct-send]])
|
||||||
@@ -23,8 +23,9 @@
|
|||||||
accounts (map :body (<! (async/into [] ar)))]
|
accounts (map :body (<! (async/into [] ar)))]
|
||||||
(reset! a-accounts accounts))))
|
(reset! a-accounts accounts))))
|
||||||
|
|
||||||
(defn shit [urls accounts]
|
(defn page-wrapper [accounts]
|
||||||
[:div.content
|
[:div.content
|
||||||
|
[:h5 "View all the assets you own."]
|
||||||
[make-portfolio-summary-table accounts]])
|
[make-portfolio-summary-table accounts]])
|
||||||
|
|
||||||
|
|
||||||
@@ -32,4 +33,4 @@
|
|||||||
(let [urls (map :skirmish.broker.account/url accounts)
|
(let [urls (map :skirmish.broker.account/url accounts)
|
||||||
a-accounts (atom [])]
|
a-accounts (atom [])]
|
||||||
(get-accounts-information urls a-accounts)
|
(get-accounts-information urls a-accounts)
|
||||||
#(shit urls @a-accounts)))
|
#(page-wrapper @a-accounts)))
|
||||||
@@ -49,7 +49,9 @@
|
|||||||
(defn create-currencies-table [securities]
|
(defn create-currencies-table [securities]
|
||||||
[:div
|
[:div
|
||||||
[:div.content
|
[:div.content
|
||||||
[:h3 "Currencies"]]
|
[:h3 "Currencies"]
|
||||||
|
[:p "A " [:em "currency"] " is currency that pays interest rate in
|
||||||
|
its own nomination."]]
|
||||||
[:table.table.is-hoverable.is-fullwidth
|
[:table.table.is-hoverable.is-fullwidth
|
||||||
table-head
|
table-head
|
||||||
[:tbody
|
[:tbody
|
||||||
@@ -58,7 +60,11 @@
|
|||||||
(defn create-equities-table [securities]
|
(defn create-equities-table [securities]
|
||||||
[:div
|
[:div
|
||||||
[:div.content
|
[:div.content
|
||||||
[:h3 "Equities"]]
|
[:h3 "Equities"]
|
||||||
|
[:p "An " [:em "equity"] " is a type of security that pays dividends. In
|
||||||
|
real life, an equity (or stock) is a share in a company ownership.
|
||||||
|
When the company make profits, it shares the its profits between its stock
|
||||||
|
holder in a pro rata fashion."]]
|
||||||
[:table.table.is-hoverable.is-fullwidth
|
[:table.table.is-hoverable.is-fullwidth
|
||||||
table-head
|
table-head
|
||||||
[:tbody
|
[:tbody
|
||||||
@@ -76,4 +82,7 @@
|
|||||||
#(create-securities-table @securities)))
|
#(create-securities-table @securities)))
|
||||||
|
|
||||||
(defn page [state]
|
(defn page [state]
|
||||||
(security-table))
|
[:div
|
||||||
|
[:div.content
|
||||||
|
[:h5 "View the available securities for trading."]]
|
||||||
|
[security-table]])
|
||||||
@@ -6,12 +6,29 @@
|
|||||||
|
|
||||||
(def done {:style {:text-decoration "line-through"}})
|
(def done {:style {:text-decoration "line-through"}})
|
||||||
|
|
||||||
|
|
||||||
|
(defn filter-out-done [n]
|
||||||
|
(let [state (volatile! n)]
|
||||||
|
(fn [item]
|
||||||
|
(when (= (second item) done)
|
||||||
|
(vswap! state dec))
|
||||||
|
(or (not= (second item) done) (neg? @state)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn todo-list [& items]
|
||||||
|
(let [filter-fn (filter-out-done (- (count items) 20))
|
||||||
|
item-to-show (filter filter-fn items)]
|
||||||
|
[:ol
|
||||||
|
(for [item item-to-show]
|
||||||
|
item)]))
|
||||||
|
|
||||||
(defn page []
|
(defn page []
|
||||||
[:div.content
|
[:div.content
|
||||||
|
[:h5 "This still to do in this project. This is a jira for the poor (or for the geek)."]
|
||||||
[:ol
|
[:ol
|
||||||
[:li "Broker"
|
[:li "Broker"
|
||||||
[:p "the brooker should use the exchange-gateway and ledge as apis. Nothing else."]
|
[:p "the brooker should use the exchange-gateway and ledge as apis. Nothing else."]
|
||||||
[:ol
|
[todo-list
|
||||||
[:li done "implement basic login in the wui"]
|
[:li done "implement basic login in the wui"]
|
||||||
[:li done "implement basic web server"]
|
[:li done "implement basic web server"]
|
||||||
[:li done "implement stupid login on server side."]
|
[:li done "implement stupid login on server side."]
|
||||||
@@ -22,14 +39,18 @@
|
|||||||
[:li done "make the " [:a {:href "/portfolio"} "portfolio"] " page representing the use accounts"]
|
[:li done "make the " [:a {:href "/portfolio"} "portfolio"] " page representing the use accounts"]
|
||||||
[:li done "make a somewhat pretty " [:a {:href "/securities/dapm"} "security"] " page"]
|
[:li done "make a somewhat pretty " [:a {:href "/securities/dapm"} "security"] " page"]
|
||||||
[:li done "create two stocks"]
|
[:li done "create two stocks"]
|
||||||
[:li on-it "add stocks to didier@gmail.com"]
|
[:li done "add stocks to didier@gmail.com"]
|
||||||
[:li on-it "make something for urls on the backend"]
|
[:li done "make something for urls on the backend"]
|
||||||
|
[:li on-it "add a wui for OTC"]
|
||||||
|
[:li "fix the stupid chat. This is an important feature for OTC users."]
|
||||||
|
[:li "make something that allows to send securities from one user to another"]
|
||||||
|
[:li "move 'routes' in 'rest'"]
|
||||||
[:li "add a little bit of professionalism on top of the broker api"]
|
[:li "add a little bit of professionalism on top of the broker api"]
|
||||||
[:li "make the api support transit and edn"]
|
[:li "make the api support transit and edn"]
|
||||||
[:li "implements interest rate in ledger."]
|
[:li "implements interest rate in ledger."]
|
||||||
[:li "Make sure the ledger can have accounts"]
|
[:li "Make sure the ledger can have accounts"]
|
||||||
[:li "when a new user signs in, he should receive some sort of money"]
|
[:li done "when a new user signs in, he should receive some sort of money"]
|
||||||
[:li "\u22EE"]
|
[:li "implement send securities to friends"]
|
||||||
[:li "implement buy/sell securities."]
|
[:li "implement buy/sell securities."]
|
||||||
[:li "Use ledger as source of through in each sub server."]
|
[:li "Use ledger as source of through in each sub server."]
|
||||||
[:li "Update web ui for integrating securities with ledger."]
|
[:li "Update web ui for integrating securities with ledger."]
|
||||||
|
|||||||
@@ -37,8 +37,9 @@
|
|||||||
[:div.navbar-start
|
[:div.navbar-start
|
||||||
[:a.navbar-item.is-tab {:href "/" :class (top-bar-class state :home)} "Home"]
|
[:a.navbar-item.is-tab {:href "/" :class (top-bar-class state :home)} "Home"]
|
||||||
[:a.navbar-item.is-tab {:href "/securities" :class (top-bar-class state :securities)} "Securities"]
|
[:a.navbar-item.is-tab {:href "/securities" :class (top-bar-class state :securities)} "Securities"]
|
||||||
[:a.navbar-item.is-tab {:href "/create-order" :class (top-bar-class state :create-order)} "Create order"]
|
[:a.navbar-item.is-tab {:href "/create-order" :class (top-bar-class state :create-order)} "Create Order"]
|
||||||
[:a.navbar-item.is-tab {:href "/portfolio" :class (top-bar-class state :view-portfolio)} "View Portfolio"]
|
[:a.navbar-item.is-tab {:href "/otc" :class (top-bar-class state :otc)} "Over The Counter"]
|
||||||
|
[:a.navbar-item.is-tab {:href "/portfolio" :class (top-bar-class state :view-portfolio)} "Portfolio"]
|
||||||
[:a.navbar-item.is-tab {:href "/todo" :class (top-bar-class state :todo)} "Todo"]]
|
[:a.navbar-item.is-tab {:href "/todo" :class (top-bar-class state :todo)} "Todo"]]
|
||||||
[:div.navbar-end
|
[:div.navbar-end
|
||||||
[:a.navbar-item {} "Skirmish Shitty Exchange"]
|
[:a.navbar-item {} "Skirmish Shitty Exchange"]
|
||||||
|
|||||||
@@ -53,6 +53,12 @@
|
|||||||
(session/put! :current-page #'pages/portfolio-page)
|
(session/put! :current-page #'pages/portfolio-page)
|
||||||
(swap! state #(into % {:page :portfolio})))
|
(swap! state #(into % {:page :portfolio})))
|
||||||
|
|
||||||
|
(secretary/defroute "/otc" []
|
||||||
|
(session/put! :current-page #'pages/otc-page)
|
||||||
|
(swap! state #(into % {:page :otc})))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(secretary/defroute "/create-order" []
|
(secretary/defroute "/create-order" []
|
||||||
(session/put! :current-page #'pages/create-order-page)
|
(session/put! :current-page #'pages/create-order-page)
|
||||||
(swap! state #(into % {:page :create-order})))
|
(swap! state #(into % {:page :create-order})))
|
||||||
@@ -77,6 +83,7 @@
|
|||||||
[(session/get :current-page) @state]]]]])
|
[(session/get :current-page) @state]]]]])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn mount-root []
|
(defn mount-root []
|
||||||
(reagent/render [current-page] (.getElementById js/document "app")))
|
(reagent/render [current-page] (.getElementById js/document "app")))
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
[skirmish.wui.components.portfolio-page]
|
[skirmish.wui.components.portfolio-page]
|
||||||
[skirmish.wui.components.create-order-page]
|
[skirmish.wui.components.create-order-page]
|
||||||
[skirmish.wui.components.securities-page]
|
[skirmish.wui.components.securities-page]
|
||||||
[skirmish.wui.components.security-page]))
|
[skirmish.wui.components.security-page]
|
||||||
|
[skirmish.wui.components.otc-page]))
|
||||||
|
|
||||||
(def home-page skirmish.wui.components.home-page/page)
|
(def home-page skirmish.wui.components.home-page/page)
|
||||||
|
|
||||||
@@ -17,3 +18,5 @@
|
|||||||
(def portfolio-page skirmish.wui.components.portfolio-page/page)
|
(def portfolio-page skirmish.wui.components.portfolio-page/page)
|
||||||
|
|
||||||
(def security-page skirmish.wui.components.security-page/page)
|
(def security-page skirmish.wui.components.security-page/page)
|
||||||
|
|
||||||
|
(def otc-page skirmish.wui.components.otc-page/page)
|
||||||
@@ -27,3 +27,8 @@
|
|||||||
(pp-int n)
|
(pp-int n)
|
||||||
(let [i (int (Math/pow 10 dec))]
|
(let [i (int (Math/pow 10 dec))]
|
||||||
(str (pp-int (quot n i)) "," (right-pad (str (mod n i)) "0" dec))))))
|
(str (pp-int (quot n i)) "," (right-pad (str (mod n i)) "0" dec))))))
|
||||||
|
|
||||||
|
(defn email? [s]
|
||||||
|
(let [email-pattern #"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"]
|
||||||
|
(->> s str (re-matches email-pattern) some?)))
|
||||||
|
|
||||||
@@ -25,6 +25,10 @@ $table-background-color: $grey-dark;
|
|||||||
$table-cell-heading-color: $grey-darker;
|
$table-cell-heading-color: $grey-darker;
|
||||||
$table-row-hover-background-color: $black-ter;
|
$table-row-hover-background-color: $black-ter;
|
||||||
|
|
||||||
|
// box
|
||||||
|
$box-background-color: $grey;
|
||||||
|
$box-color: $white;
|
||||||
|
$box-shadow: 0 2px 3px $grey-light, 0 0 0 1px $grey-light;// $red;
|
||||||
|
|
||||||
@import "../../node_modules/bulma/sass/base/_all";
|
@import "../../node_modules/bulma/sass/base/_all";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
(ns skirmish.broker.broker-test
|
(ns skirmish.broker.broker-test
|
||||||
(:require [midje.sweet :refer :all]
|
(:require [midje.sweet :refer :all]
|
||||||
[orchestra.spec.test :as st]
|
[orchestra.spec.test :as st]
|
||||||
[skirmish.broker.broker :refer [add-account-urls]]))
|
[skirmish.broker.broker :refer [add-account-urls
|
||||||
|
create-user-seed-from-email
|
||||||
|
generate-amount-for-new-user]]))
|
||||||
|
|
||||||
(st/instrument)
|
(st/instrument)
|
||||||
|
|
||||||
@@ -13,3 +15,19 @@
|
|||||||
:skirmish.broker.account/url "http://localhost:1604/api/1/ledger/accounts/foo"}]}]
|
:skirmish.broker.account/url "http://localhost:1604/api/1/ledger/accounts/foo"}]}]
|
||||||
(add-account-urls user) => ret)))
|
(add-account-urls user) => ret)))
|
||||||
|
|
||||||
|
(facts "about the new users"
|
||||||
|
(fact "we should add some random amount of damp in their account"
|
||||||
|
(let [random-values (map generate-amount-for-new-user (range 10000))
|
||||||
|
sum (reduce + random-values)
|
||||||
|
mean (/ sum (count random-values))
|
||||||
|
min-data (apply min random-values)
|
||||||
|
max-data (apply max random-values)]
|
||||||
|
(int mean) => 100027427
|
||||||
|
min-data => 78422400
|
||||||
|
max-data => 120128000))
|
||||||
|
|
||||||
|
(fact "we should generate the random seed by the email"
|
||||||
|
(create-user-seed-from-email "foo@gmail.com") => 399122929
|
||||||
|
(create-user-seed-from-email "boo@gmail.com") => 1433675645))
|
||||||
|
|
||||||
|
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
(:require [midje.sweet :refer :all]
|
(:require [midje.sweet :refer :all]
|
||||||
[orchestra.spec.test :as st]
|
[orchestra.spec.test :as st]
|
||||||
[datomic.api :as d]
|
[datomic.api :as d]
|
||||||
[skirmish.broker.db :refer [create-connection]]))
|
[skirmish.broker.db :refer [create-connection]]
|
||||||
|
[skirmish.broker.broker :refer [create-account]]))
|
||||||
|
|
||||||
(st/instrument)
|
(st/instrument)
|
||||||
|
|
||||||
@@ -15,7 +16,10 @@
|
|||||||
(fact "should be able to create new users"
|
(fact "should be able to create new users"
|
||||||
(let [conn (create-conn)
|
(let [conn (create-conn)
|
||||||
email "foo@gmail.com"
|
email "foo@gmail.com"
|
||||||
_ @(d/transact conn [[:skirmish.broker.user/create-user email]])
|
account-id #uuid"dc0d1123-e659-4e8a-b38b-ece520f4c4d0"
|
||||||
|
account (create-account account-id :dapm)
|
||||||
|
_ @(d/transact conn [[:skirmish.broker.user/create-user email account]])
|
||||||
db (d/db conn)
|
db (d/db conn)
|
||||||
user (d/pull db '[*] [:skirmish.broker.user/email email])]
|
user (d/pull db '[* {:skirmish.broker.user/accounts [*]}] [:skirmish.broker.user/email email])]
|
||||||
(:skirmish.broker.user/email user) => email)));
|
(:skirmish.broker.user/email user) => email
|
||||||
|
(-> user :skirmish.broker.user/accounts first :skirmish.broker.account/id) => account-id)))
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
(ns skirmish.cli.benchmark-test
|
|
||||||
(:require [skirmish.cli.benchmark :refer :all]
|
|
||||||
[midje.sweet :refer :all]
|
|
||||||
[clojure.core.async :as async]))
|
|
||||||
|
|
||||||
(facts "creating simple orders"
|
|
||||||
(fact "buy order"
|
|
||||||
(let [buy-order (create-requests-payload-for-security 666 (str :equity/ibm) 1)
|
|
||||||
expt {:benchmark/id 1 :price 2922 :quantity 2476 :security ":equity/ibm" :type :buy}]
|
|
||||||
buy-order => expt));;
|
|
||||||
(fact "sell order"
|
|
||||||
(let [buy-order (create-requests-payload-for-security 667 (str :equity/ibm) 2)
|
|
||||||
expt {:benchmark/id 2 :price 2740 :quantity 2516 :security ":equity/ibm" :type :sell}]
|
|
||||||
buy-order => expt)))
|
|
||||||
|
|
||||||
(facts "the utility functions"
|
|
||||||
(fact "no-more-then-n-simultaneously"
|
|
||||||
(let [simultatious (atom [])
|
|
||||||
input-chan (async/to-chan (range 10))
|
|
||||||
sleep-time #(+ (mod (+ (* 5142 %) 8765) 200) 50)
|
|
||||||
f (fn [i]
|
|
||||||
(swap! simultatious #(conj % {:id i :status :start :value 1}))
|
|
||||||
(Thread/sleep (sleep-time i))
|
|
||||||
(swap! simultatious #(conj % {:id i :status :end :value -1}))
|
|
||||||
i)
|
|
||||||
res (async/<!! (async/into [] (no-more-then-n-simultaneously f input-chan 3)))
|
|
||||||
simultatious-over-time (reductions #(+ %1 (:value %2)) 0 @simultatious)]
|
|
||||||
(apply max simultatious-over-time) => 3
|
|
||||||
(first simultatious-over-time) => 0
|
|
||||||
(last simultatious-over-time) => 0)))
|
|
||||||
Reference in new issue
Block a user