parent
2ebe8bcd1f
commit
4a1507eba4
25 changed files with 215 additions and 58 deletions
@ -0,0 +1,5 @@ |
|||||||
|
.dank-form-item { |
||||||
|
label { |
||||||
|
color: white; |
||||||
|
} |
||||||
|
} |
@ -1,7 +1,9 @@ |
|||||||
import { InputNumber as AntInputNumber, InputNumberProps } from 'antd' |
import { InputNumber as AntInputNumber, InputNumberProps } from 'antd' |
||||||
|
|
||||||
|
import './style.scss' |
||||||
|
|
||||||
type Props = InputNumberProps |
type Props = InputNumberProps |
||||||
|
|
||||||
export const InputNumber = (props: Props) => { |
export const InputNumber = (props: Props) => { |
||||||
return <AntInputNumber {...props} /> |
return <AntInputNumber className="dank-input-number" {...props} /> |
||||||
} |
} |
||||||
|
@ -0,0 +1,33 @@ |
|||||||
|
.ant-input-number { |
||||||
|
display: grid; |
||||||
|
grid-template: 1fr / 1fr; |
||||||
|
|
||||||
|
div { |
||||||
|
grid-area: 1/1/2/2; |
||||||
|
} |
||||||
|
|
||||||
|
&-handler { |
||||||
|
&-wrap { |
||||||
|
z-index: 2; |
||||||
|
width: 100%; |
||||||
|
pointer-events: none; |
||||||
|
display: flex; |
||||||
|
flex-direction: row; |
||||||
|
justify-content: flex-end; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
|
||||||
|
&-up, |
||||||
|
&-down { |
||||||
|
pointer-events: all; |
||||||
|
cursor: pointer; |
||||||
|
margin: auto 0.5rem auto 0; |
||||||
|
svg { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&-input-wrap { |
||||||
|
z-index: 1; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
import { useForm } from 'antd/lib/form/Form' |
||||||
|
import { Button } from '../elements/Button' |
||||||
|
import { Form } from '../elements/Form' |
||||||
|
import { FormItem } from '../elements/FormItem' |
||||||
|
import { Input } from '../elements/Input' |
||||||
|
import { InputNumber } from '../elements/InputNumber' |
||||||
|
import { |
||||||
|
useCreateTransaction, |
||||||
|
NewTransaction, |
||||||
|
} from '../hooks/create/useCreateTransaction' |
||||||
|
import { DankFormProps } from '../layout/Modal' |
||||||
|
import { Row } from '../layout/Row' |
||||||
|
|
||||||
|
type Props = DankFormProps & { |
||||||
|
stackId: string |
||||||
|
} |
||||||
|
|
||||||
|
export const TransactionForm = ({ stackId, onSave, onCancel }: Props) => { |
||||||
|
const [form] = useForm<NewTransaction>() |
||||||
|
const create = useCreateTransaction() |
||||||
|
|
||||||
|
const handleFinish = async (tx: NewTransaction) => { |
||||||
|
console.log('transaction', tx) |
||||||
|
const res = await create(tx) |
||||||
|
|
||||||
|
if (!res) { |
||||||
|
console.log("couldn't create transaction") |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
onSave?.() |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<Form |
||||||
|
form={form} |
||||||
|
onFinish={handleFinish} |
||||||
|
initialValues={{ stack: stackId }} |
||||||
|
> |
||||||
|
<h1>New Transaction</h1> |
||||||
|
<FormItem label="Amount" name="amount"> |
||||||
|
<InputNumber /> |
||||||
|
</FormItem> |
||||||
|
<FormItem label="Details" name="amount"> |
||||||
|
<Input /> |
||||||
|
</FormItem> |
||||||
|
<Row> |
||||||
|
<Button onClick={onCancel}>Cancel</Button> |
||||||
|
<Button htmlType="submit">Save</Button> |
||||||
|
</Row> |
||||||
|
</Form> |
||||||
|
) |
||||||
|
} |
@ -1,9 +1,11 @@ |
|||||||
import { useAppContext } from '../../contexts/AppContext' |
import { useAppContext } from '../../contexts/AppContext' |
||||||
import { Transaction } from '../../types' |
import { Transaction } from '../../types' |
||||||
|
|
||||||
|
export type NewTransaction = Omit<Transaction, 'id'> |
||||||
|
|
||||||
export const useCreateTransaction = () => { |
export const useCreateTransaction = () => { |
||||||
const api = useAppContext() |
const api = useAppContext() |
||||||
|
|
||||||
return (body: Omit<Transaction, 'id'>): Promise<Transaction> => |
return (body: NewTransaction): Promise<Transaction | null> => |
||||||
api.post<Transaction>('/transactions', body) |
api.post<Transaction>('/transactions/', body) |
||||||
} |
} |
||||||
|
@ -1,4 +1,5 @@ |
|||||||
import { Transaction } from '../../types' |
import { Transaction } from '../../types' |
||||||
import { useGet } from '../util/useGet' |
import { useGet } from '../util/useGet' |
||||||
|
|
||||||
export const useTransactions = () => useGet<Transaction[]>(`/transactions/`) |
export const useTransactions = () => |
||||||
|
useGet<Transaction[]>(`/transactions/`, { mode: 'list' }) |
||||||
|
@ -1,9 +1,16 @@ |
|||||||
import { ModalProps } from 'antd' |
import { ModalProps } from 'antd' |
||||||
import { ReactNode } from 'react' |
import { ReactElement, cloneElement } from 'react' |
||||||
import './style.scss' |
import './style.scss' |
||||||
|
|
||||||
type Props = ModalProps & { children: ReactNode } |
type Props = ModalProps & { children: ReactElement } |
||||||
|
|
||||||
export const Modal = ({ children, ...props }: Props) => { |
export type DankFormProps = { |
||||||
return <div className="dank-modal">{children}</div> |
onSave?: () => void |
||||||
|
onCancel?: () => void |
||||||
|
} |
||||||
|
|
||||||
|
export const Modal = ({ children, visible }: Props) => { |
||||||
|
const el = cloneElement(children, (props: DankFormProps) => ({ ...props })) |
||||||
|
|
||||||
|
return visible ? <div className="dank-modal">{el}</div> : null |
||||||
} |
} |
||||||
|
@ -0,0 +1,10 @@ |
|||||||
|
import React from 'react' |
||||||
|
import './style.scss' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
children: React.ReactNode |
||||||
|
} |
||||||
|
|
||||||
|
export const Row = ({ children }: Props) => { |
||||||
|
return <div className="dank-row">{children}</div> |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
.dank-row { |
||||||
|
display: flex; |
||||||
|
flex-direction: row; |
||||||
|
justify-content: space-between; |
||||||
|
margin: 1rem 0; |
||||||
|
} |
@ -1,40 +1,47 @@ |
|||||||
import { Stack } from '../../types' |
import { Stack } from '../../types' |
||||||
import './style.scss' |
import './style.scss' |
||||||
import _ from 'lodash' |
import _ from 'lodash' |
||||||
|
import { useState } from 'react' |
||||||
|
import { Modal } from '../../layout/Modal' |
||||||
|
import { TransactionForm } from '../../forms/TransactionForm' |
||||||
|
|
||||||
type Props = { |
type Props = { |
||||||
stack: Stack |
stack: Stack |
||||||
} |
} |
||||||
|
|
||||||
export const FundBar = ({ stack }: Props) => { |
export const FundBar = ({ stack }: Props) => { |
||||||
const amount = _.sumBy(stack.transactions, 'amount') |
const amount = _.sumBy(stack.transactions, (tx) => parseFloat(tx.amount)) |
||||||
console.log('amount', stack.id, amount) |
const max = parseFloat(stack.amount) |
||||||
const max = stack.amount |
|
||||||
const current = max - amount |
const current = max - amount |
||||||
const percent = Math.max(current / max, 0) |
const u = Math.max(current / max, 0) |
||||||
const hue = percent * 120 |
|
||||||
|
const [newTx, setNewTx] = useState(false) |
||||||
|
|
||||||
|
const addTransaction = () => { |
||||||
|
console.log(`adding transaction to => ${stack.name}`) |
||||||
|
setNewTx(true) |
||||||
|
} |
||||||
|
|
||||||
return ( |
return ( |
||||||
<div className="fundbar"> |
<> |
||||||
<div |
<div className="fundbar"> |
||||||
className={`back`} |
<div className="back" onClick={addTransaction}> |
||||||
onClick={() => console.log(`adding transaction to => ${stack.name}`)} |
<h3>{stack.name}</h3> |
||||||
></div> |
</div> |
||||||
<div |
<div |
||||||
className={`front`} |
className="front" |
||||||
style={{ |
style={{ |
||||||
background: `hsl(${hue}, 100%, 50%)`, |
background: `hsl(${u * 120}, 100%, 50%)`, |
||||||
height: `${percent * 40 + 10}vmin`, |
height: `${u * 100}%`, |
||||||
}} |
}} |
||||||
> |
></div> |
||||||
<h3>{stack.name}</h3> |
<div className="totals"> |
||||||
</div> |
${Math.floor(current)} / ${max} |
||||||
<div |
</div> |
||||||
className={`totals`} |
|
||||||
style={{ color: `hsl(0, 0%, ${Math.abs(1 - percent) * 100}%)` }} |
|
||||||
> |
|
||||||
${Math.floor(current)} / ${max} |
|
||||||
</div> |
</div> |
||||||
</div> |
<Modal visible={newTx} onCancel={() => setNewTx(false)}> |
||||||
|
<TransactionForm stackId={stack.id} /> |
||||||
|
</Modal> |
||||||
|
</> |
||||||
) |
) |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue