diff --git a/client/package.json b/client/package.json index 3084258..e8c356f 100644 --- a/client/package.json +++ b/client/package.json @@ -9,6 +9,7 @@ "react": "^16.8.6", "react-dom": "^16.8.6", "react-materialize": "^3.3.1", + "react-router-dom": "^5.2.0", "react-scripts": "3.0.1" }, "scripts": { diff --git a/client/src/App.js b/client/src/App.js index e0abfa8..89a5c22 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -2,16 +2,19 @@ import React from 'react'; import './App.css'; import { Navbar } from 'react-materialize'; import MakeMoneyMove from './cash/MakeMoneyMove'; +import Flow from "./homies/Flow"; import Cash from './cash/Cash'; import Chips from './chips/Chips'; import { BrowserRouter as Router, Route } from "react-router-dom"; import RoutableNavItem from './common/RoutableNavItem'; -import { +import { CRIB, CHIPS, - MAKE_MONEY_MOVE + MAKE_MONEY_MOVE, + HOMIE_FLOW } from './RouteNames'; + function App() { return ( @@ -37,6 +40,7 @@ function App() {
+
diff --git a/client/src/RouteNames.js b/client/src/RouteNames.js index d84b419..5ce2b05 100644 --- a/client/src/RouteNames.js +++ b/client/src/RouteNames.js @@ -1,3 +1,4 @@ export const CRIB = '/'; export const CHIPS = '/chips'; export const MAKE_MONEY_MOVE = '/make-money-move'; +export const HOMIE_FLOW = '/homie/:homie_id/flow'; diff --git a/client/src/cash/Cash.js b/client/src/cash/Cash.js index 617a21c..a234b69 100644 --- a/client/src/cash/Cash.js +++ b/client/src/cash/Cash.js @@ -4,6 +4,7 @@ import './Cash.css'; import axios from 'axios'; import { MAKE_MONEY_MOVE } from '../RouteNames'; import { withRouter } from 'react-router-dom'; +import { formatMoney } from '../common/formatting'; const Cash = (props) => { const [homiesCash, setHomiesCash] = useState([]); @@ -21,17 +22,11 @@ const Cash = (props) => { getCashForHomies(); }, []); - - const formatMoney = (amount) => { - const formatted = Number.parseFloat(amount).toFixed(2); - return `${formatted} KM`; - } - const cashTableBody = homiesCash.map( (homieLine) => { return ( - { homieLine.homie.name } + { homieLine.homie.name } { formatMoney(homieLine.amount) } @@ -44,39 +39,39 @@ const Cash = (props) => { }); return ( -
+
+ + + + + + + + + + { cashTableBody } + +
+ Homie + + Cash + + Actions +
- - - - - - - - - - { cashTableBody } - -
- Homie - - Cash - - Actions -
- -
); diff --git a/client/src/cash/MakeMoneyMove.js b/client/src/cash/MakeMoneyMove.js index c208f11..ac8f499 100644 --- a/client/src/cash/MakeMoneyMove.js +++ b/client/src/cash/MakeMoneyMove.js @@ -1,6 +1,6 @@ import React, {useState, useEffect} from 'react'; import M from 'materialize-css'; -import {Icon, Select, TextInput, Button, Textarea} from 'react-materialize'; +import {Icon, Select, Button, Textarea} from 'react-materialize'; import './Cash.css'; import axios from 'axios'; diff --git a/client/src/common/formatting.js b/client/src/common/formatting.js new file mode 100644 index 0000000..d9055e6 --- /dev/null +++ b/client/src/common/formatting.js @@ -0,0 +1,16 @@ +const formatMoney = (amount) => { + const formatted = Number.parseFloat(amount).toFixed(2); + return `${formatted} KM`; +} + +const timestampToDate = (timestamp) => { + const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' }; + + const date = new Date(timestamp); + return date.toLocaleDateString('en-GB', dateOptions); +} + +export { + formatMoney, + timestampToDate +} \ No newline at end of file diff --git a/client/src/homies/Flow.css b/client/src/homies/Flow.css new file mode 100644 index 0000000..0ce704e --- /dev/null +++ b/client/src/homies/Flow.css @@ -0,0 +1,34 @@ +.amount-green { + background-color: rgba(72, 172, 152, 0.35); + padding: 3px 5px; + font-weight: 500; + color: rgb(72, 172, 152); + border-radius: 5px; +} + +.opposite-sides-content { + width: 100%; + justify-content: space-between; +} + +.flex-row { + display: flex; + flex-direction: row; + align-items: stretch; +} + +.flex-center { + align-self: center; +} + +.flex-col { + display: flex; + flex-direction: column; + align-items: stretch; +} + +.record:hover { + border-left: rgb(72, 172, 152) solid 4px; + box-sizing: border-box; + padding-left: 11px; +} \ No newline at end of file diff --git a/client/src/homies/Flow.js b/client/src/homies/Flow.js new file mode 100644 index 0000000..3ccb4f0 --- /dev/null +++ b/client/src/homies/Flow.js @@ -0,0 +1,78 @@ +import React, { useState, useEffect } from 'react'; +import { withRouter, useParams } from 'react-router-dom'; +import axios from "axios"; +import M from "materialize-css"; +import './Flow.css'; +import { formatMoney, timestampToDate } from "../common/formatting"; + +const Flow = (props) => { + const { homie_id } = useParams(); + + const [flow, setFlow] = useState([]); + + useEffect( () => { + (async () => { + try { + const response = await axios.get(`/api/money_moves?homie_id=${parseInt(homie_id)}`); + if (response.status === 200 && response.data){ + setFlow(response.data); + } + } catch (e) { + M.toast({ html: "Yo! It ain't workin'" }); + } + })(); + }, [homie_id]); + + const dateBlock = (timestamp) => { timestampToDate(timestamp) } + + + const flowData = flow.map( (singleFlowData, index) => ( +
  • +
    +
    +
    +
    { singleFlowData.description }
    +
    { dateBlock(singleFlowData['created_at']) }
    +
    + +
    0 ? 'amount-green' : ''}`}>{ formatMoney(singleFlowData.amount) }
    +
    +
    +
  • + )); + + const totalStats = () => { + const totalCount = flow.length; + const firstFlowRow = totalCount > 0 ? flow[0] : undefined; + const lastFlowRow = totalCount > 0 ? flow[flow.length - 1] : undefined; + const totalFlow = flow.reduce((sum, record) => sum + parseInt(record.amount), 0); + + const fromDate = lastFlowRow ? timestampToDate(lastFlowRow['created_at']) : ''; + const toDate = firstFlowRow ? timestampToDate(firstFlowRow['created_at']) : ''; + + return ( +
    +
    +
    +
    + {`${totalCount} Records`}{` • ${fromDate} - ${toDate}`} +
    +
    + Total flow: {formatMoney(totalFlow)} +
    +
    +
    + ) + } + + return ( +
    + { totalStats() } +
      + { flowData } +
    +
    + ); +} + +export default withRouter(Flow); \ No newline at end of file diff --git a/client/yarn.lock b/client/yarn.lock index b1ef9c6..c9e8f21 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -859,6 +859,13 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.1.2", "@babel/runtime@^7.5.5": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" @@ -4449,6 +4456,18 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -4458,6 +4477,13 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hoist-non-react-statics@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" @@ -5068,6 +5094,11 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -5903,7 +5934,7 @@ loglevel@^1.4.1: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" integrity sha1-4PyVEztu8nbNyIh82vJKpvFW+Po= -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -6097,6 +6128,14 @@ mimic-fn@^2.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mini-create-react-context@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz#df60501c83151db69e28eac0ef08b4002efab040" + integrity sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA== + dependencies: + "@babel/runtime" "^7.5.5" + tiny-warning "^1.0.3" + mini-css-extract-plugin@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" @@ -6845,6 +6884,13 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -7893,6 +7939,11 @@ react-error-overlay@^5.1.6: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.6.tgz#0cd73407c5d141f9638ae1e0c63e7b2bf7e9929d" integrity sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q== +react-is@^16.6.0, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^16.8.1, react-is@^16.8.4: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" @@ -7907,6 +7958,35 @@ react-materialize@^3.3.1: react "^16.8.5" react-dom "^16.8.5" +react-router-dom@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" + integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.2.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" + integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.4.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + react-scripts@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.0.1.tgz#e5565350d8069cc9966b5998d3fe3befe3d243ac" @@ -8078,6 +8158,11 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + regenerator-transform@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb" @@ -8261,6 +8346,11 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -9126,6 +9216,16 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tiny-invariant@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" + integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== + +tiny-warning@^1.0.0, tiny-warning@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -9484,6 +9584,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"