Skip to content

Commit

Permalink
add initial code for invoice create / edit
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardobeat committed Mar 31, 2023
1 parent 0ea5046 commit 0e79cf1
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'

import InvoicesList from './components/InvoicesList'
import InvoiceShow from './components/InvoiceShow'
import InvoiceCreate from './components/InvoiceCreate'
import InvoiceEdit from './components/InvoiceEdit'
import Header from './components/Header'

function App() {
Expand All @@ -10,7 +12,9 @@ function App() {
<Header />
<Router>
<Switch>
<Route path="/invoice/new" component={InvoiceCreate} />
<Route path="/invoice/:id" component={InvoiceShow} />
<Route path="/invoice/:id/edit" component={InvoiceEdit} />
<Route path="/" component={InvoicesList} />
</Switch>
</Router>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/CustomerAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const CustomerAutocomplete = ({ value, onChange }: Props) => {

return (
<AsyncPaginate
placeholder="Search a customer"
placeholder={`ex: "Alejandro"`}
getOptionLabel={getCustomerLabel}
getOptionValue={getOptionValue}
additional={defaultAdditional}
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Stack from 'react-bootstrap/Stack'
import Container from 'react-bootstrap/Container'

export default function Header() {
return (
<header>
<Stack className="mt-5">
<Container className="mt-5 mb-5">
<h1>Fern Hill</h1>
<p className="text-muted">Invoices</p>
</Stack>
</Container>
</header>
)
}
61 changes: 61 additions & 0 deletions src/app/components/InvoiceCreate/InvoiceLineForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useState } from 'react'
import type { Product } from '../../../types'

import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import Form from 'react-bootstrap/Form'
import Card from 'react-bootstrap/Card'

import ProductAutocomplete from '../ProductAutocomplete'

export function InvoiceLineForm() {
const [selectedProduct, setSelectedProduct] = useState<Product>()

return (
<Card className="mt-3">
<Card.Header>Add product</Card.Header>
<Card.Body>
<Row className="mb-3">
<ProductAutocomplete onChange={setSelectedProduct} />
</Row>
<Row className="mb-3">
<Form.Group as={Col} controlId="inputTax">
<Form.Label className="text-end">Quantity</Form.Label>
<Form.Control type="text" />
</Form.Group>

<Form.Group as={Col} controlId="inputTax">
<Form.Label>Unit</Form.Label>
<Form.Control type="text" />
</Form.Group>

<Form.Group as={Row} controlId="inputTax">
<Form.Label>Label</Form.Label>
<Form.Control type="text" />
</Form.Group>

<Form.Group as={Row} className="mb-3" controlId="inputTax">
<Form.Label column sm={2}>
VAT Rate
</Form.Label>
<Form.Control type="text" />
</Form.Group>

<Form.Group as={Row} className="mb-3" controlId="inputTax">
<Form.Label column sm={2}>
Price
</Form.Label>
<Form.Control type="text" readOnly />
</Form.Group>

<Form.Group as={Row} className="mb-3" controlId="inputTax">
<Form.Label column sm={2}>
Tax
</Form.Label>
<Form.Control type="text" />
</Form.Group>
</Row>
</Card.Body>
</Card>
)
}
141 changes: 141 additions & 0 deletions src/app/components/InvoiceCreate/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { useState } from 'react'

import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import Button from 'react-bootstrap/Button'
import Form from 'react-bootstrap/Form'
import InputGroup from 'react-bootstrap/esm/InputGroup'
import Container from 'react-bootstrap/esm/Container'

import CustomerAutocomplete from '../CustomerAutocomplete'
import { Invoice } from 'types'
import { formatCustomerAddress } from 'app/lib/formatting'
import ProductAutocomplete from '../ProductAutocomplete'
import { InvoiceLineForm } from './InvoiceLineForm'

const InvoiceCreate = () => {
// const api = useApi()
// const [invoice, setInvoice] = useState<Invoice>()

const [formState, setFormState] = useState<Invoice>({
id: 0,
customer_id: null,
finalized: false,
paid: false,
date: null,
deadline: null,
total: null,
tax: null,
customer: undefined,
invoice_lines: [],
})

const setCustomer = (customer: NonNullable<Invoice['customer']>) => {
setFormState((s) => ({
...s,
customer_id: customer.id,
customer,
}))
}

const addProduct = (product: NonNullable<Invoice['invoice_lines'][0]>) => {
setFormState((s) => ({
...s,
invoice_lines: [...s.invoice_lines, product],
}))
}

const [addingInvoiceLine, setAddingInvoiceLine] = useState(false)

return (
<Container>
<header className="mb-5">
<h2>New invoice</h2>
</header>

<Form>
<Form.Group as={Row} className="mb-3" controlId="inputCustomer">
<Form.Label column sm={2} className="text-end">
Customer
</Form.Label>
<Col sm={10}>
<CustomerAutocomplete
value={formState.customer}
onChange={setCustomer}
/>
{formState.customer ? (
<div className="pt-2 pb-2">
<strong>Address: </strong>
<pre>
{formatCustomerAddress(formState.customer)}
<br />
{formState.customer.country}
</pre>
</div>
) : null}
</Col>
</Form.Group>

<Form.Group as={Row} className="mb-3" controlId="inputTotal">
<Form.Label column sm={2} className="text-end">
Total Amount
</Form.Label>

<Col sm={10}>
<InputGroup>
<InputGroup.Text></InputGroup.Text>
<Form.Control
type="text"
inputMode="numeric"
placeholder="0,00"
/>
</InputGroup>
</Col>
</Form.Group>

<Form.Group as={Row} className="mb-3" controlId="inputTax">
<Form.Label column sm={2} className="text-end">
Tax
</Form.Label>

<Col sm={10}>
<InputGroup>
<InputGroup.Text></InputGroup.Text>
<Form.Control
type="text"
inputMode="numeric"
placeholder="0,00"
/>
</InputGroup>
</Col>
</Form.Group>

<Form.Group as={Row} className="mb-3" controlId="inputTax">
<Form.Label column sm={2} className="text-end">
Items
</Form.Label>

<Col sm={10}>
{addingInvoiceLine && <InvoiceLineForm />}
<Button
variant="secondary"
onClick={() => setAddingInvoiceLine(true)}
>
Add product
</Button>
</Col>
</Form.Group>

<Row className="mt-5">
<Col sm={{ span: 10, offset: 2 }}>
<Button variant="primary" type="submit">
Create invoice
</Button>
</Col>
</Row>
</Form>
</Container>
)
}

export default InvoiceCreate
25 changes: 25 additions & 0 deletions src/app/components/InvoiceEdit/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, useEffect } from 'react'
import { useParams } from 'react-router'

import { useApi } from 'api'
import { Invoice } from 'types'

const InvoiceShow = () => {
const { id } = useParams<{ id: string }>()
const api = useApi()
const [invoice, setInvoice] = useState<Invoice>()

useEffect(() => {
api.getInvoice(id).then(({ data }) => {
setInvoice(data)
})
}, [api, id])

return (
<div>
<pre>{JSON.stringify(invoice ?? '', null, 2)}</pre>
</div>
)
}

export default InvoiceShow
9 changes: 6 additions & 3 deletions src/app/components/InvoicesList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useCallback } from 'react'

import Container from 'react-bootstrap/Container'
import Alert from 'react-bootstrap/Alert'

import Button from 'react-bootstrap/Button'
import Stack from 'react-bootstrap/Stack'
import Table from 'react-bootstrap/Table'
import Spinner from 'react-bootstrap/Spinner'
Expand Down Expand Up @@ -39,7 +38,11 @@ const InvoicesList = (): React.ReactElement => {

return (
<Stack gap={3}>
<Container></Container>
<Stack direction="horizontal" className="justify-content-end" gap={3}>
<Button onClick={() => history.push(`/invoice/new`)}>
Create invoice
</Button>
</Stack>
<Table striped bordered hover>
<thead>
<tr>
Expand Down

0 comments on commit 0e79cf1

Please sign in to comment.