motorin along
This commit is contained in:
parent
8ff02f6149
commit
01a1876b3d
|
@ -1,6 +1,6 @@
|
|||
import { useUserContext } from './contexts/UserContext'
|
||||
import { Route, Switch } from 'react-router'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Link, useHistory } from 'react-router-dom'
|
||||
import { Dashboard } from './pages/Dashboard'
|
||||
import { NewUser } from './pages/NewUser'
|
||||
import { TransactionList } from './pages/TransactionList'
|
||||
|
@ -8,29 +8,32 @@ import { AccountForm } from './forms/AccountForm'
|
|||
import { Login } from './pages/Login'
|
||||
import { AppHeader } from './layout/AppHeader'
|
||||
import { AppFooter } from './layout/AppFooter'
|
||||
import { TransactionForm } from './pages/TransactionForm'
|
||||
import { Button } from 'antd'
|
||||
|
||||
export const CoreLayout = () => {
|
||||
const { user } = useUserContext()
|
||||
const history = useHistory()
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<AppHeader>
|
||||
<Link to="/">Home</Link>
|
||||
<Link to="/select">Select Budget</Link>
|
||||
<Link to="/accounts">Budget Details</Link>
|
||||
<Link to="/accounts">Accounts</Link>
|
||||
<Button onClick={() => history.push('?new=account')}>New</Button>
|
||||
<Link to="/transactions">Transactions</Link>
|
||||
<Link to="/new">New User</Link>
|
||||
<Button onClick={() => history.push('?new=transaction')}>New</Button>
|
||||
</AppHeader>
|
||||
|
||||
<main>
|
||||
{!user ? (
|
||||
<Switch>
|
||||
<Route path="/sign-up" component={NewUser} />
|
||||
<Route path="/" component={Login} />
|
||||
<Route path="/login" component={Login} />
|
||||
<div>loading...</div>
|
||||
</Switch>
|
||||
) : (
|
||||
<Switch>
|
||||
<Route path="/users/new" component={NewUser} />
|
||||
<Route path="/transactions/new" component={TransactionForm} />
|
||||
<Route path="/transactions" component={TransactionList} />
|
||||
<Route path="/accounts/new" component={AccountForm} />
|
||||
<Route path="/accounts" component={AccountForm} />
|
||||
|
|
|
@ -12,6 +12,7 @@ type AppContextInterface = {
|
|||
post: <T, R = T>(path: string, body: Partial<T>) => Promise<R>
|
||||
create: <T>(path: string, body: Partial<T>) => Promise<T>
|
||||
destroy: (path: string) => Promise<string>
|
||||
baseURL?: string
|
||||
}
|
||||
|
||||
const AppContext = createContext<AppContextInterface | null>(null)
|
||||
|
@ -52,7 +53,7 @@ export const AppContextProvider = ({ children, baseURL }: Props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{ get, patch, post, create, destroy }}>
|
||||
<AppContext.Provider value={{ get, patch, post, create, destroy, baseURL }}>
|
||||
{children}
|
||||
</AppContext.Provider>
|
||||
)
|
||||
|
|
|
@ -3,7 +3,8 @@ import { message } from 'antd'
|
|||
import { useHistory } from 'react-router'
|
||||
import { User } from '../types'
|
||||
import { useAppContext } from './AppContext'
|
||||
import { logOut } from '../api'
|
||||
import { useEffect } from 'react'
|
||||
import { useRef } from 'react'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
|
@ -18,14 +19,36 @@ type Context = {
|
|||
const UserContext = createContext<Context | null>(null)
|
||||
|
||||
export const UserContextProvider = ({ children }: Props) => {
|
||||
const api = useAppContext()
|
||||
const history = useHistory()
|
||||
const api = useRef(useAppContext())
|
||||
const history = useRef(useHistory())
|
||||
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const get = async () => {
|
||||
try {
|
||||
const token = window.localStorage.getItem('cash-stacks-token')
|
||||
if (!token) throw new Error()
|
||||
|
||||
// const valid = await api.current.post('/dj-rest-auth/token/verify/', {
|
||||
// token,
|
||||
// })
|
||||
// console.log('token valid?', token)
|
||||
const user = await api.current.get<User>(`/dj-rest-auth/user/`)
|
||||
setUser(user)
|
||||
} catch (err) {
|
||||
console.log(`couldn't get user, token possibly expired`)
|
||||
// window.localStorage.removeItem('cash-stacks-token')
|
||||
history.current.push('/login')
|
||||
}
|
||||
}
|
||||
|
||||
get()
|
||||
}, [])
|
||||
|
||||
const handleLogin = async (username: string, password: string) => {
|
||||
try {
|
||||
const { key } = await api.post<any, { key: string }>(
|
||||
const { key } = await api.current.post<any, { key: string }>(
|
||||
'/dj-rest-auth/login/',
|
||||
{
|
||||
username,
|
||||
|
@ -34,24 +57,18 @@ export const UserContextProvider = ({ children }: Props) => {
|
|||
)
|
||||
if (!key) throw new Error('Problem logging in!')
|
||||
window.localStorage.setItem('cash-stacks-token', key)
|
||||
const user = await api.get<User>(`/dj-rest-auth/user/`)
|
||||
|
||||
const user = await api.current.get<User>(`/dj-rest-auth/user/`)
|
||||
console.log('user', user)
|
||||
|
||||
if (!user) message.error(`Couldn't find user`)
|
||||
setUser(user)
|
||||
|
||||
message.success(`logged in as ${user?.name}`, 0.5)
|
||||
} catch (err) {
|
||||
message.error('Login Failed!')
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logOut()
|
||||
await api.current.post('/dj-rest-auth/logout/', {})
|
||||
setUser(null)
|
||||
message.success('logged out!', 0.5)
|
||||
history.push('/')
|
||||
history.current.push('/')
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
8
frontend/src/elements/FormItem/index.tsx
Normal file
8
frontend/src/elements/FormItem/index.tsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { FormItemProps } from 'antd'
|
||||
import AntFormItem from 'antd/lib/form/FormItem'
|
||||
|
||||
type Props = FormItemProps
|
||||
|
||||
export const FormItem = (props: Props) => {
|
||||
return <AntFormItem className="dank-form-item" {...props}></AntFormItem>
|
||||
}
|
7
frontend/src/elements/Input/index.tsx
Normal file
7
frontend/src/elements/Input/index.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { Input as AntInput, InputProps } from 'antd'
|
||||
|
||||
type Props = InputProps
|
||||
|
||||
export const Input = (props: Props) => {
|
||||
return <AntInput className="dank-input" {...props}></AntInput>
|
||||
}
|
7
frontend/src/elements/InputNumber/index.tsx
Normal file
7
frontend/src/elements/InputNumber/index.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { InputNumber as AntInputNumber, InputNumberProps } from 'antd'
|
||||
|
||||
type Props = InputNumberProps
|
||||
|
||||
export const InputNumber = (props: Props) => {
|
||||
return <AntInputNumber {...props} />
|
||||
}
|
|
@ -9,7 +9,7 @@ type Props = {
|
|||
}
|
||||
|
||||
export const AccountForm = ({ account }: Props) => {
|
||||
const stacks = useStacks('')
|
||||
const stacks = useStacks()
|
||||
|
||||
const [name, setName] = useState<string>(account?.name || '')
|
||||
const [details, setDetails] = useState<string>(account?.details || '')
|
||||
|
|
|
@ -1,88 +1,39 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
|
||||
import { Password, User } from '../types'
|
||||
import { User } from '../types'
|
||||
import { useUserContext } from '../contexts/UserContext'
|
||||
import { message } from 'antd'
|
||||
import { Button } from '../elements/Button'
|
||||
import { Form } from '../elements/Form'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import { FormItem } from '../elements/FormItem'
|
||||
import { Input } from '../elements/Input'
|
||||
|
||||
export const UserForm = () => {
|
||||
const { user } = useUserContext()
|
||||
if (!user) throw new Error('User should be gotten by this point')
|
||||
|
||||
console.log(user)
|
||||
const [form] = useForm<User>()
|
||||
|
||||
const [name, setName] = useState(user?.name)
|
||||
const [email, setEmail] = useState(user?.email)
|
||||
const [password, setPassword] = useState('')
|
||||
const [passwordConfirm, setPasswordConfirm] = useState('')
|
||||
const [valid, setValid] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
passwordConfirm.length > 4 && password === passwordConfirm && name && email
|
||||
? setValid(true)
|
||||
: setValid(false)
|
||||
}, [password, passwordConfirm, name, email])
|
||||
|
||||
// @ts-ignore
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!name || !email) {
|
||||
message.error('Name and email required!')
|
||||
return
|
||||
}
|
||||
|
||||
const body: Omit<User, 'id'> & Password = {
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
passwordConfirm,
|
||||
}
|
||||
|
||||
try {
|
||||
// user?.id
|
||||
// ? await api.updateUser(user.id, body)
|
||||
// : await api.createUser(body)
|
||||
console.log('User form wtf')
|
||||
// if (!user?.id) history.push('/login')
|
||||
} catch (err) {
|
||||
message.error('Something went wrong')
|
||||
}
|
||||
const handleFinish = (values: User) => {
|
||||
console.log(values)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>{user?.id ? 'Edit' : 'Create'} Profile</h1>
|
||||
<form className="dank-form" onSubmit={handleSubmit}>
|
||||
<label>
|
||||
Name: <input value={name} onChange={(e) => setName(e.target.value)} />
|
||||
</label>
|
||||
<label>
|
||||
Email:
|
||||
<input
|
||||
value={email}
|
||||
type="email"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Password:
|
||||
<input
|
||||
value={password}
|
||||
type="password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Confirm Password:
|
||||
<input
|
||||
type="password"
|
||||
onChange={(e) => setPasswordConfirm(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<Button htmlType="submit" disabled={!valid}>
|
||||
Save
|
||||
</Button>
|
||||
</form>
|
||||
<Form form={form} onFinish={handleFinish} initialValues={user}>
|
||||
<h1>User Profile</h1>
|
||||
<FormItem label="Username" name="username">
|
||||
<Input />
|
||||
</FormItem>
|
||||
<FormItem label="First Name" name="first_name">
|
||||
<Input />
|
||||
</FormItem>
|
||||
<FormItem label="Last Name" name="last_name">
|
||||
<Input />
|
||||
</FormItem>
|
||||
<FormItem label="E-Mail" name="email">
|
||||
<Input type="email" />
|
||||
</FormItem>
|
||||
<Button htmlType="submit">Save</Button>
|
||||
</Form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Account } from '../../types'
|
||||
import { useGet } from '../util/useGet'
|
||||
|
||||
export const useAccounts = () => useGet<Account[]>(`/accounts`)
|
||||
export const useAccounts = () => useGet<Account[]>(`/accounts/`)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Stack } from '../../types'
|
||||
import { useGet } from '../util/useGet'
|
||||
|
||||
export const useStacks = (accountId: string) => useGet<Stack[]>(`/stacks`)
|
||||
export const useStacks = () => useGet<Stack[]>(`/stacks/`)
|
||||
|
|
|
@ -2,4 +2,4 @@ import { Transaction } from '../../types'
|
|||
import { useGet } from '../util/useGet'
|
||||
|
||||
export const useTransactions = (accountId: string) =>
|
||||
useGet<Transaction[]>(`/accounts/${accountId}/transactions`)
|
||||
useGet<Transaction[]>(`/transactions/`)
|
||||
|
|
|
@ -8,8 +8,8 @@ export const useGet = <T>(path: string) => {
|
|||
const get = useCallback(async () => {
|
||||
const data = await appContext.current.get<T>(path)
|
||||
if (!data) return
|
||||
|
||||
setData(data)
|
||||
// @ts-ignore
|
||||
setData(data.results)
|
||||
}, [path])
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -7,7 +7,7 @@ export const AppFooter = () => {
|
|||
|
||||
return (
|
||||
<footer>
|
||||
<p>User: {user?.name}</p>
|
||||
<p>User: {user?.username}</p>
|
||||
<p>|</p>
|
||||
<p>Budget:</p>
|
||||
<p>|</p>
|
||||
|
|
|
@ -2,9 +2,9 @@ import { ReactNode } from 'react'
|
|||
import { Avatar, Dropdown, Menu } from 'antd'
|
||||
import { Link, useHistory } from 'react-router-dom'
|
||||
import { useUserContext } from '../../contexts/UserContext'
|
||||
import { Button } from '../../elements/Button'
|
||||
|
||||
import './style.scss'
|
||||
import { Button } from '../../elements/Button'
|
||||
|
||||
type Props = {
|
||||
children: ReactNode
|
||||
|
@ -27,16 +27,14 @@ export const AppHeader = ({ children }: Props) => {
|
|||
<header>
|
||||
<div className="header-left">
|
||||
<Link to="/">
|
||||
<h3>MVP Django React! 🤠</h3>
|
||||
<h3>CashStacks! 💵</h3>
|
||||
</Link>
|
||||
{children}
|
||||
</div>
|
||||
<div>
|
||||
{/* <Button onClick={() => history.push('/new/user')}>New User</Button> */}
|
||||
</div>
|
||||
<nav>{children}</nav>
|
||||
{user && (
|
||||
<div className="header-right">
|
||||
<p>Welcome, {user.name}!!</p>
|
||||
<Dropdown
|
||||
<p>Welcome, {user?.username}!!</p>
|
||||
{/* <Dropdown
|
||||
overlay={
|
||||
<Menu style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Button onClick={() => history.push('/login')}>Login</Button>
|
||||
|
@ -44,13 +42,16 @@ export const AppHeader = ({ children }: Props) => {
|
|||
<Button onClick={() => history.push('/forgot-password')}>
|
||||
Forgot Password
|
||||
</Button>
|
||||
<Button onClick={() => history.push('/profile')}>Profile</Button>
|
||||
<Button onClick={() => history.push('/profile')}>
|
||||
Profile
|
||||
</Button>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Avatar>{user.name[0]}</Avatar>
|
||||
</Dropdown>
|
||||
<Avatar>{user.username[0]}</Avatar>
|
||||
</Dropdown> */}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,10 +2,23 @@
|
|||
|
||||
header {
|
||||
display: flex;
|
||||
padding: 0.5rem;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: $color-dark;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
nav {
|
||||
a {
|
||||
margin: auto 0.5rem;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: $color-light;
|
||||
|
|
9
frontend/src/layout/Modal/index.tsx
Normal file
9
frontend/src/layout/Modal/index.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { ModalProps } from 'antd'
|
||||
import { ReactNode } from 'react'
|
||||
import './style.scss'
|
||||
|
||||
type Props = ModalProps & { children: ReactNode }
|
||||
|
||||
export const Modal = ({ children, ...props }: Props) => {
|
||||
return <div className="dank-modal">{children}</div>
|
||||
}
|
16
frontend/src/layout/Modal/style.scss
Normal file
16
frontend/src/layout/Modal/style.scss
Normal file
|
@ -0,0 +1,16 @@
|
|||
@import '../../scss/variables.scss';
|
||||
|
||||
.dank-modal {
|
||||
display: flex;
|
||||
border: 1px solid $color-grey;
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
color: $color-light;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { FundBar } from '../../widgets/FundBar'
|
|||
import { useStacks } from '../../hooks/getMany/useStacks'
|
||||
|
||||
export const Dashboard = () => {
|
||||
const stacks = useStacks('42')
|
||||
const stacks = useStacks()
|
||||
|
||||
if (!stacks.data) return <div>loading...</div>
|
||||
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
import { InputNumber } from 'antd'
|
||||
import { useState } from 'react'
|
||||
import { Button } from '../../elements/Button'
|
||||
|
||||
import '../../scss/transaction-modal.scss'
|
||||
type Props = {
|
||||
stackId: string
|
||||
}
|
||||
|
||||
export const TransactionForm = ({ stackId }: Props) => {
|
||||
const [amount, setAmount] = useState(0)
|
||||
const [stack, setStack] = useState(stackId)
|
||||
const [details, setDetails] = useState('')
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!amount) return
|
||||
|
||||
const body = {
|
||||
stack,
|
||||
details,
|
||||
amount: amount,
|
||||
created_at: new Date().toISOString(),
|
||||
}
|
||||
|
||||
console.log('creating tx', body)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="transaction-modal">
|
||||
<form onSubmit={handleSubmit} className="form">
|
||||
<div className="cancel-button">X</div>
|
||||
<h3>Expense for {stack} account</h3>
|
||||
<label>
|
||||
Amount:
|
||||
<InputNumber
|
||||
autoFocus
|
||||
value={amount}
|
||||
onChange={(v) => setAmount(v)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Details:
|
||||
<input
|
||||
value={details}
|
||||
onChange={(e) => setDetails(e.currentTarget.value)}
|
||||
></input>
|
||||
</label>
|
||||
<label>
|
||||
<select value={stack} onChange={(e) => setStack(e.target.value)}>
|
||||
<option value={-1} disabled>
|
||||
Select Account
|
||||
</option>
|
||||
</select>
|
||||
<Button htmlType="button">cancel</Button>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
.transaction-modal {
|
||||
color: #fff;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
width: 95vw;
|
||||
height: 95vh;
|
||||
border-radius: 1ch;
|
||||
border: 1ch solid #000;
|
||||
background: #000c;
|
||||
display: flex;
|
||||
pointer-events: visible;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
text-shadow: 2px 2px #000, -1px -1px #444;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
cursor: pointer;
|
||||
background: #000a;
|
||||
border: 1px solid #ccc;
|
||||
width: 4ch;
|
||||
padding: 1ch 1ch;
|
||||
border-radius: 1.5ch;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
top: -9ch;
|
||||
left: -2ch;
|
||||
}
|
||||
|
||||
.form {
|
||||
pointer-events: all;
|
||||
box-sizing: content-box;
|
||||
padding: 3ch 2ch;
|
||||
background: #000a;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 1ch;
|
||||
width: 50%;
|
||||
margin: auto;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
margin: 1ch;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,20 @@
|
|||
import { Input, message } from 'antd'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import FormItem from 'antd/lib/form/FormItem'
|
||||
import axios from 'axios'
|
||||
import { useAppContext } from '../../contexts/AppContext'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { Form } from '../../elements/Form'
|
||||
import { User } from '../../types'
|
||||
|
||||
type NewUserForm = Omit<User, 'id'> & {
|
||||
type NewUserForm = {
|
||||
username: string
|
||||
email: string
|
||||
password1: string
|
||||
password2: string
|
||||
}
|
||||
|
||||
export const NewUser = () => {
|
||||
const api = useAppContext()
|
||||
const { baseURL } = useAppContext()
|
||||
const [form] = useForm<NewUserForm>()
|
||||
|
||||
const handleFinish = (user: NewUserForm) => {
|
||||
|
@ -21,7 +23,7 @@ export const NewUser = () => {
|
|||
return
|
||||
}
|
||||
|
||||
api.post(`/dj-rest-auth/registration`, user)
|
||||
axios.post(`${baseURL}/dj-rest-auth/registration/`, user)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -29,11 +31,11 @@ export const NewUser = () => {
|
|||
<FormItem label="username" name="username">
|
||||
<Input></Input>
|
||||
</FormItem>
|
||||
<FormItem label="email" name="email">
|
||||
{/* <FormItem label="email" name="email">
|
||||
<Input type="email"></Input>
|
||||
</FormItem>
|
||||
</FormItem> */}
|
||||
<FormItem label="password" name="password1">
|
||||
<Input minLength={8}></Input>
|
||||
<Input minLength={6}></Input>
|
||||
</FormItem>
|
||||
<FormItem label="confirm" name="password2">
|
||||
<Input></Input>
|
||||
|
|
55
frontend/src/pages/TransactionForm/index.tsx
Normal file
55
frontend/src/pages/TransactionForm/index.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { useForm } from 'antd/lib/form/Form'
|
||||
import { useState } from 'react'
|
||||
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 { useStacks } from '../../hooks/getMany/useStacks'
|
||||
import { Modal } from '../../layout/Modal'
|
||||
|
||||
import { Transaction } from '../../types'
|
||||
|
||||
type Props = {
|
||||
stackId: string
|
||||
}
|
||||
|
||||
export const TransactionForm = ({ stackId }: Props) => {
|
||||
const [form] = useForm<Transaction>()
|
||||
const [visible, setVisible] = useState(true)
|
||||
const stacks = useStacks()
|
||||
|
||||
const handleFinish = (tx: Transaction) => {
|
||||
console.log('creating tx ', tx)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal onCancel={handleClose} visible={visible}>
|
||||
<Form onFinish={handleFinish} form={form} className="form">
|
||||
<h3>Expense for {form.getFieldValue('stack')} account</h3>
|
||||
<FormItem label="Amount" name="amount">
|
||||
<InputNumber autoFocus />
|
||||
</FormItem>
|
||||
<FormItem label="Details" name="details">
|
||||
<Input />
|
||||
</FormItem>
|
||||
<FormItem name="Stack" label="Stack">
|
||||
<select>
|
||||
{stacks.data?.map((stack) => (
|
||||
<option value={stack.id}>{stack.name}</option>
|
||||
))}
|
||||
<option value={-1} disabled>
|
||||
Select Account
|
||||
</option>
|
||||
</select>
|
||||
</FormItem>
|
||||
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
|
@ -1,8 +1,16 @@
|
|||
export type ApiResponse<T> = {
|
||||
count: number
|
||||
next: number | null
|
||||
previous: number | null
|
||||
results: T[]
|
||||
}
|
||||
|
||||
export type User = {
|
||||
id: string
|
||||
name: string
|
||||
id: number
|
||||
username: string
|
||||
email: string
|
||||
// password: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
}
|
||||
|
||||
export type Account = {
|
||||
|
|
Loading…
Reference in New Issue
Block a user