diff --git a/app/controllers/work_controller.rb b/app/controllers/work_controller.rb
new file mode 100644
index 0000000..259a863
--- /dev/null
+++ b/app/controllers/work_controller.rb
@@ -0,0 +1,31 @@
+class WorkController < ApplicationController
+ def index
+ if params[:homie_id]
+ work = Work.where(homie_id: params[:homie_id].to_i).all.order(created_at: :desc)
+ else
+ work = Work.all.order(created_at: :desc)
+ end
+
+ json_response work
+ end
+
+ def create
+ work = Work.new(work_params)
+
+ if work.save
+ json_response ok: true
+ else
+ error_response(:bad_request)
+ end
+ end
+
+ private
+
+ def work_params
+ params.require(:work).permit(
+ :description,
+ :amount,
+ :homie_id
+ )
+ end
+end
\ No newline at end of file
diff --git a/app/models/homie.rb b/app/models/homie.rb
index 16aab9c..9d5d5f6 100644
--- a/app/models/homie.rb
+++ b/app/models/homie.rb
@@ -1,5 +1,6 @@
class Homie < ApplicationRecord
has_many :money_moves
+ has_many :work
belongs_to :chip
def self.cash(importance)
diff --git a/app/models/work.rb b/app/models/work.rb
new file mode 100644
index 0000000..7f7337e
--- /dev/null
+++ b/app/models/work.rb
@@ -0,0 +1,3 @@
+class Work < ApplicationRecord
+ belongs_to :homie
+end
\ No newline at end of file
diff --git a/client/src/App.js b/client/src/App.js
index ee9060c..e831832 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -13,8 +13,9 @@ import {
CHIPS,
MAKE_MONEY_MOVE,
HOMIE_FLOW,
- HOMIES
+ HOMIES, PUT_IN_WORK
} from './RouteNames';
+import PutInWork from "./cash/PutInWork";
function App() {
@@ -37,6 +38,10 @@ function App() {
Make Money Move
+
+
+ Put in Work
+
@@ -46,6 +51,7 @@ function App() {
+
);
diff --git a/client/src/RouteNames.js b/client/src/RouteNames.js
index d956223..ae706c3 100644
--- a/client/src/RouteNames.js
+++ b/client/src/RouteNames.js
@@ -2,4 +2,5 @@ export const CRIB = '/';
export const HOMIES = '/homies';
export const CHIPS = '/chips';
export const MAKE_MONEY_MOVE = '/make-money-move';
+export const PUT_IN_WORK = '/put-in-work';
export const HOMIE_FLOW = '/homie/:homie_id/flow';
diff --git a/client/src/cash/PutInWork.js b/client/src/cash/PutInWork.js
new file mode 100644
index 0000000..62f9c1b
--- /dev/null
+++ b/client/src/cash/PutInWork.js
@@ -0,0 +1,97 @@
+import React, {useState, useEffect} from 'react';
+import M from 'materialize-css';
+import {Icon, Select, Button, Textarea} from 'react-materialize';
+import './Cash.css';
+import axios from 'axios';
+import {errorToast} from "../common/errorHelpers";
+
+const PutInWork = (props) => {
+ const [homies, setHomies] = useState([]);
+ const [selectedTo, setSelectedTo] = useState('');
+ const [amountToAdd, setAmountToAdd] = useState('');
+ const [workDescription, setWorkDescription] = useState('');
+ const [submitInProgress, setSubmitInProgress] = useState(false);
+
+ useEffect(() => {
+ (async () => {
+ try {
+ const response = await axios.get('/api/homies');
+ if (response.status === 200 && response.data){
+ setHomies(response.data);
+ }
+ } catch (e) {
+ errorToast();
+ }
+ })();
+ }, []);
+
+ const homieOptions = homies.map(homie => );
+
+ const clearForm = () => {
+ setAmountToAdd('');
+ setSelectedTo('');
+ setWorkDescription('');
+ }
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setSubmitInProgress(true);
+ const workRequest = {
+ work: {
+ amount: parseInt(amountToAdd),
+ homie_id: selectedTo,
+ description: workDescription
+ }
+ }
+
+ const submitResponse = await axios.post('/api/work', workRequest);
+
+ if (submitResponse && submitResponse.status === 200 && submitResponse.data && submitResponse.data.ok === true) {
+ M.toast({html: "Alright"});
+ clearForm();
+ } else {
+ errorToast();
+ }
+ setSubmitInProgress(false);
+ }
+
+ const formComplete = () => selectedTo !== '' && parseInt(amountToAdd) !== 0 && !isNaN(parseInt(amountToAdd));
+
+ const disableSubmit = () => (!formComplete() || submitInProgress);
+
+ return (
+
+ );
+
+}
+
+export default PutInWork;
diff --git a/client/src/common/formatting.js b/client/src/common/formatting.js
index d9055e6..1f64e6c 100644
--- a/client/src/common/formatting.js
+++ b/client/src/common/formatting.js
@@ -3,6 +3,11 @@ const formatMoney = (amount) => {
return `${formatted} KM`;
}
+const formatTime = (amount) => {
+ const formatted = Number.parseInt(amount);
+ return `${formatted} hrs`;
+}
+
const timestampToDate = (timestamp) => {
const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' };
@@ -12,5 +17,6 @@ const timestampToDate = (timestamp) => {
export {
formatMoney,
+ formatTime,
timestampToDate
}
\ No newline at end of file
diff --git a/client/src/homies/CashFlow.js b/client/src/homies/CashFlow.js
new file mode 100644
index 0000000..b09f62c
--- /dev/null
+++ b/client/src/homies/CashFlow.js
@@ -0,0 +1,77 @@
+import React, { useState, useEffect } from 'react';
+import { withRouter, useParams } from 'react-router-dom';
+import axios from "axios";
+import './Flow.css';
+import { formatMoney, timestampToDate } from "../common/formatting";
+import {errorToast} from "../common/errorHelpers";
+
+const CashFlow = (props) => {
+ const { homie_id } = useParams();
+
+ const [cashFlow, setCashFlow] = useState([]);
+
+ useEffect( () => {
+ (async () => {
+ try {
+ const response = await axios.get(`/api/money_moves?homie_id=${parseInt(homie_id)}`);
+ if (response.status === 200 && response.data){
+ setCashFlow(response.data);
+ }
+ } catch (e) {
+ errorToast();
+ }
+ })();
+ }, [homie_id]);
+
+ const dateBlock = (timestamp) => { timestampToDate(timestamp) }
+
+ const flowData = cashFlow.map( (singleFlowData, index) => (
+
+
+
+
+
{ singleFlowData.description }
+
{ dateBlock(singleFlowData['created_at']) }
+
+
+
0 ? 'amount-green' : ''}`}>{ formatMoney(singleFlowData.amount) }
+
+
+
+ ));
+
+ const totalStats = () => {
+ const totalCount = cashFlow.length;
+ const firstFlowRow = totalCount > 0 ? cashFlow[0] : undefined;
+ const lastFlowRow = totalCount > 0 ? cashFlow[cashFlow.length - 1] : undefined;
+ const totalFlow = cashFlow.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 cash flow: {formatMoney(totalFlow)}
+
+
+
+ )
+ }
+
+ return (
+
+ );
+}
+
+export default withRouter(CashFlow);
\ No newline at end of file
diff --git a/client/src/homies/Flow.css b/client/src/homies/Flow.css
index 0ce704e..9950c3e 100644
--- a/client/src/homies/Flow.css
+++ b/client/src/homies/Flow.css
@@ -31,4 +31,9 @@
border-left: rgb(72, 172, 152) solid 4px;
box-sizing: border-box;
padding-left: 11px;
+}
+
+.switch-box {
+ margin-top: 10px;
+ margin-bottom: 20px;
}
\ No newline at end of file
diff --git a/client/src/homies/Flow.js b/client/src/homies/Flow.js
index 3ccb4f0..87a82d9 100644
--- a/client/src/homies/Flow.js
+++ b/client/src/homies/Flow.js
@@ -1,76 +1,26 @@
-import React, { useState, useEffect } from 'react';
-import { withRouter, useParams } from 'react-router-dom';
-import axios from "axios";
-import M from "materialize-css";
+import React, { useState } from 'react';
+import { withRouter } from 'react-router-dom';
+import { Switch } from 'react-materialize';
import './Flow.css';
-import { formatMoney, timestampToDate } from "../common/formatting";
+import CashFlow from "./CashFlow";
+import WorkFlow from "./WorkFlow";
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)}
-
-
-
- )
- }
+ const [flowType, setFlowType] = useState('cash');
return (
- { totalStats() }
-
+
+ setFlowType(e.target.checked === true ? 'work' : 'cash')}
+ onLabel="Work"
+ />
+
+
+ { flowType === 'cash' &&
}
+ { flowType === 'work' &&
}
+
);
}
diff --git a/client/src/homies/WorkFlow.js b/client/src/homies/WorkFlow.js
new file mode 100644
index 0000000..35b3c2a
--- /dev/null
+++ b/client/src/homies/WorkFlow.js
@@ -0,0 +1,77 @@
+import React, { useState, useEffect } from 'react';
+import { withRouter, useParams } from 'react-router-dom';
+import axios from "axios";
+import './Flow.css';
+import { formatTime, timestampToDate } from "../common/formatting";
+import {errorToast} from "../common/errorHelpers";
+
+const WorkFlow = (props) => {
+ const { homie_id } = useParams();
+
+ const [work, setWork] = useState([]);
+
+ useEffect( () => {
+ (async () => {
+ try {
+ const response = await axios.get(`/api/work?homie_id=${parseInt(homie_id)}`);
+ if (response.status === 200 && response.data){
+ setWork(response.data);
+ }
+ } catch (e) {
+ errorToast();
+ }
+ })();
+ }, [homie_id]);
+
+ const dateBlock = (timestamp) => { timestampToDate(timestamp) }
+
+ const flowData = work.map( (singleWorkData, index) => (
+
+
+
+
+
{ singleWorkData.description }
+
{ dateBlock(singleWorkData['created_at']) }
+
+
+
0 ? 'amount-green' : ''}`}>{ formatTime(singleWorkData.amount) }
+
+
+
+ ));
+
+ const totalStats = () => {
+ const totalCount = work.length;
+ const firstFlowRow = totalCount > 0 ? work[0] : undefined;
+ const lastFlowRow = totalCount > 0 ? work[work.length - 1] : undefined;
+ const totalFlow = work.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 work: {formatTime(totalFlow)}
+
+
+
+ )
+ }
+
+ return (
+
+ );
+}
+
+export default withRouter(WorkFlow);
\ No newline at end of file
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index ac033bf..ab4f265 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -14,3 +14,7 @@
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym 'RESTful'
# end
+
+ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular 'work', 'work'
+end
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index 1a36dfd..e3f2417 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,6 +2,7 @@ Rails.application.routes.draw do
constraints format: :json do
scope :api do
resources :money_moves
+ resources :work
resources :chips, only: %i[index create destroy]
resources :chip_values, only: %i[create update destroy]
resources :homies, param: :homie_id do
diff --git a/db/migrate/20200919023237_create_work.rb b/db/migrate/20200919023237_create_work.rb
new file mode 100644
index 0000000..963a5f7
--- /dev/null
+++ b/db/migrate/20200919023237_create_work.rb
@@ -0,0 +1,11 @@
+class CreateWork < ActiveRecord::Migration[5.2]
+ def change
+ create_table :work do |t|
+ t.text :description
+ t.integer :amount
+ t.integer :homie_id, null: false
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 52ccf15..41dcfe5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_09_11_171657) do
+ActiveRecord::Schema.define(version: 2020_09_19_023237) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -53,6 +53,14 @@ ActiveRecord::Schema.define(version: 2020_09_11_171657) do
t.datetime "updated_at", null: false
end
+ create_table "work", force: :cascade do |t|
+ t.text "description"
+ t.integer "amount"
+ t.integer "homie_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
add_foreign_key "chip_values", "chips", column: "base_chip_id"
add_foreign_key "chip_values", "chips", column: "secondary_chip_id"
end