Skip to content

Commit

Permalink
Merge pull request #1 from hammerframework/pp-add-billable-frontend
Browse files Browse the repository at this point in the history
Add Billable frontend
  • Loading branch information
peterp authored Jul 4, 2019
2 parents d2b836d + aa58462 commit 40f3d8a
Show file tree
Hide file tree
Showing 14 changed files with 821 additions and 8 deletions.
3 changes: 2 additions & 1 deletion web/.babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"src": "./src"
}
}
]
],
"babel-plugin-styled-components"
]
}
11 changes: 8 additions & 3 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@
"private": true,
"dependencies": {
"@hammerframework/hammer-web": "0.0.0",
"immer": "^3.1.3",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"styled-components": "^4.3.2"
"react-textarea-autosize": "^7.1.0",
"rebass": "^3.1.1",
"rebass-extend": "^1.0.1",
"styled-components": "^4.3.2",
"styled-system": "^5.0.12"
},
"devDependencies": {
"directory-named-webpack-plugin": "^4.0.1",
"babel-loader": "^8.0.6",
"babel-plugin-module-resolver": "^3.2.0",
"babel-plugin-styled-components": "^1.10.5",
"babel-plugin-styled-components": "^1.10.6",
"css-loader": "^3.0.0",
"directory-named-webpack-plugin": "^4.0.1",
"file-loader": "^4.0.0",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^0.23.1",
Expand Down
10 changes: 7 additions & 3 deletions web/src/App.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import ReactDOM from "react-dom";
import { GraphQLProvider, Query } from "@hammerframework/hammer-web";
import { GraphQLProvider } from "@hammerframework/hammer-web";
import { ThemeProvider } from "styled-components";

import "./global.css";
import Help from "src/components/Help";
import theme from "src/lib/theme";
import InvoicePage from "src/pages";

// TODO: Add Router
ReactDOM.render(
<GraphQLProvider>
<Query component={Help} />
<ThemeProvider theme={theme}>
<InvoicePage />
</ThemeProvider>
</GraphQLProvider>,
document.getElementById("hammer-app")
);
23 changes: 23 additions & 0 deletions web/src/components/AppBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Box } from "src/lib/primitives";

export default () => (
<Box
bg="blue"
css={`
height: 64px;
`}
>
<Box
color="white"
fontSize={5}
pl="8px"
m="auto"
css={`
width: 800px;
line-height: 64px;
`}
>
Billable
</Box>
</Box>
);
41 changes: 41 additions & 0 deletions web/src/components/InvoiceInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react'
import produce from 'immer'

import { Box } from 'src/lib/primitives'
import { TextInput } from 'src/components'

const InvoiceInfo = ({ value, onChange, ...rest }) => {
const handleChange = (rowIndex, colIndex) => newValue =>
onChange(
produce(value, draft => {
draft[rowIndex][colIndex].value = newValue
})
)

return (
<Box as="table" {...rest}>
<Box as="tbody">
{value.map((row, rowIndex) => (
<Box as="tr" key={`invoice-info-${rowIndex}`}>
<Box as="th" width={0.5}>
<TextInput
onChange={handleChange(rowIndex, 0)}
width={1}
{...row[0]}
/>
</Box>
<Box as="td">
<TextInput
onChange={handleChange(rowIndex, 1)}
width={1}
{...row[1]}
/>
</Box>
</Box>
))}
</Box>
</Box>
)
}

export default InvoiceInfo
128 changes: 128 additions & 0 deletions web/src/components/LineItems.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React from 'react'
import produce from 'immer'

import { Box, Button } from 'src/lib/primitives'
import { TextInput } from 'src/components'

const LineItems = ({ value, onChange, ...rest }) => {
const handleChange = (rowIndex, colIndex) => newValue =>
onChange(
produce(value, draft => {
draft[rowIndex][colIndex].value = newValue
})
)

return (
<Box
as="table"
{...rest}
css={`
tr:first-child input {
font-weight: bold;
}
td {
border-bottom-width: 0;
}
`}
>
<tbody>
{value.map((row, rowIndex) => (
<tr
key={`invoice-info-${rowIndex}`}
css={`
vertical-align: middle;
`}
>
<Box as="td" width="400px">
<TextInput
multiline={rowIndex !== 0}
onChange={handleChange(rowIndex, 0)}
width={1}
{...row[0]}
/>
</Box>
<td>
<TextInput
onChange={handleChange(rowIndex, 1)}
width={1}
type={rowIndex !== 0 ? 'number' : 'text'}
{...row[1]}
/>
</td>
<td>
<TextInput
onChange={handleChange(rowIndex, 2)}
width={1}
type={rowIndex !== 0 ? 'number' : 'text'}
{...row[2]}
/>
</td>
{rowIndex !== 0 && (
<Box
as="td"
width="48px"
textAlign="right"
css={`
vertical-align: middle;
padding-right: 8px;
border-color: transparent;
`}
>
<Button
width="24px"
onClick={() =>
onChange(
produce(value, draft => {
delete draft[rowIndex]
})
)
}
>
-
</Button>
</Box>
)}
</tr>
))}

<tr>
<td
colSpan="3"
css={`
border-width: 0;
border-top-width: 1px;
`}
/>
<td
css={`
vertical-align: middle;
padding-right: 8px;
border-color: transparent;
`}
>
<Box textAlign="right">
<Button
width="24px"
onClick={() =>
onChange(
produce(value, draft => {
draft.push([
{ value: '' },
{ value: 1 },
{ value: '0.0' },
])
})
)
}
>
+
</Button>
</Box>
</td>
</tr>
</tbody>
</Box>
)
}

export default LineItems
114 changes: 114 additions & 0 deletions web/src/components/Summary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import React from 'react'
import produce from 'immer'

import { Box } from 'src/lib/primitives'
import { TextInput } from 'src/components'

const calculateSubtotal = lineItems => {
return lineItems
.slice(1) // remove heading items
.map(([_, { value: quantity }, { value: price }]) => [
Number(quantity),
Number(price),
])
.reduce((subtotal, [quantity, price]) => (subtotal += quantity * price), 0)
}

const calculatePercentage = (subtotal, row) => {
const percentage = Number(row[1].value)
return subtotal * (percentage / 100)
}

const calculateTotal = (subtotal, rows) => {
// we only have 1 value at the moment
const percentage = Number(rows[0][1].value)
return subtotal + subtotal * (percentage / 100)
}

const Summary = ({ value, onChange, lineItems, ...rest }) => {
calculateSubtotal(lineItems)

const handleChange = (rowIndex, colIndex) => newValue =>
onChange(
produce(value, draft => {
draft[rowIndex][colIndex].value = newValue
})
)

const subtotalRow = value.slice(0, 1)[0]
const subtotal = calculateSubtotal(lineItems)
const rows = value.slice(1, -1)
const totalRow = value.slice(-1)[0]

return (
<Box
as="table"
{...rest}
width={0.5}
css={`
td:nth-child(3) {
padding-left: 8px;
}
`}
>
<Box as="tbody">
<Box as="tr">
<Box as="th" width="199px">
<TextInput
onChange={handleChange(0, 0)}
width={1}
{...subtotalRow[0]}
/>
</Box>
<Box as="td" width="100px">
&nbsp;
</Box>
<Box as="td">{subtotal.toFixed(2)}</Box>
</Box>

{rows.map((row, rowIndex) => (
<Box as="tr" key={`invoice-info-${rowIndex}`}>
<Box as="th">
<TextInput
onChange={handleChange(rowIndex + 1, 0)}
width={1}
{...row[0]}
/>
</Box>
<Box as="td">
{row[1] && (
<TextInput
onChange={handleChange(rowIndex + 1, 1)}
width={1}
type="number"
{...row[1]}
/>
)}
</Box>
<Box as="td">{calculatePercentage(subtotal, row).toFixed(2)}</Box>
</Box>
))}

<Box as="tr">
<Box as="th">
<TextInput
onChange={handleChange(value.length - 1, 0)}
width={1}
{...totalRow[0]}
/>
</Box>
<Box as="td">
<TextInput
onChange={handleChange(value.length - 1, 1)}
width={1}
{...totalRow[1]}
/>
</Box>
<Box as="td">{calculateTotal(subtotal, rows).toFixed(2)}</Box>
</Box>
</Box>
</Box>
)
}

export default Summary
Loading

0 comments on commit 40f3d8a

Please sign in to comment.