motorin along
This commit is contained in:
parent
8ff02f6149
commit
01a1876b3d
|
@ -1,6 +1,6 @@
|
||||||
import { useUserContext } from './contexts/UserContext'
|
import { useUserContext } from './contexts/UserContext'
|
||||||
import { Route, Switch } from 'react-router'
|
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 { Dashboard } from './pages/Dashboard'
|
||||||
import { NewUser } from './pages/NewUser'
|
import { NewUser } from './pages/NewUser'
|
||||||
import { TransactionList } from './pages/TransactionList'
|
import { TransactionList } from './pages/TransactionList'
|
||||||
|
@ -8,29 +8,32 @@ import { AccountForm } from './forms/AccountForm'
|
||||||
import { Login } from './pages/Login'
|
import { Login } from './pages/Login'
|
||||||
import { AppHeader } from './layout/AppHeader'
|
import { AppHeader } from './layout/AppHeader'
|
||||||
import { AppFooter } from './layout/AppFooter'
|
import { AppFooter } from './layout/AppFooter'
|
||||||
|
import { TransactionForm } from './pages/TransactionForm'
|
||||||
|
import { Button } from 'antd'
|
||||||
|
|
||||||
export const CoreLayout = () => {
|
export const CoreLayout = () => {
|
||||||
const { user } = useUserContext()
|
const { user } = useUserContext()
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<AppHeader>
|
<AppHeader>
|
||||||
<Link to="/">Home</Link>
|
<Link to="/accounts">Accounts</Link>
|
||||||
<Link to="/select">Select Budget</Link>
|
<Button onClick={() => history.push('?new=account')}>New</Button>
|
||||||
<Link to="/accounts">Budget Details</Link>
|
|
||||||
<Link to="/transactions">Transactions</Link>
|
<Link to="/transactions">Transactions</Link>
|
||||||
<Link to="/new">New User</Link>
|
<Button onClick={() => history.push('?new=transaction')}>New</Button>
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
{!user ? (
|
{!user ? (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/sign-up" component={NewUser} />
|
<Route path="/sign-up" component={NewUser} />
|
||||||
<Route path="/" component={Login} />
|
<Route path="/login" component={Login} />
|
||||||
|
<div>loading...</div>
|
||||||
</Switch>
|
</Switch>
|
||||||
) : (
|
) : (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/users/new" component={NewUser} />
|
<Route path="/transactions/new" component={TransactionForm} />
|
||||||
<Route path="/transactions" component={TransactionList} />
|
<Route path="/transactions" component={TransactionList} />
|
||||||
<Route path="/accounts/new" component={AccountForm} />
|
<Route path="/accounts/new" component={AccountForm} />
|
||||||
<Route path="/accounts" component={AccountForm} />
|
<Route path="/accounts" component={AccountForm} />
|
||||||
|
|
|
@ -12,6 +12,7 @@ type AppContextInterface = {
|
||||||
post: <T, R = T>(path: string, body: Partial<T>) => Promise<R>
|
post: <T, R = T>(path: string, body: Partial<T>) => Promise<R>
|
||||||
create: <T>(path: string, body: Partial<T>) => Promise<T>
|
create: <T>(path: string, body: Partial<T>) => Promise<T>
|
||||||
destroy: (path: string) => Promise<string>
|
destroy: (path: string) => Promise<string>
|
||||||
|
baseURL?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppContext = createContext<AppContextInterface | null>(null)
|
const AppContext = createContext<AppContextInterface | null>(null)
|
||||||
|
@ -52,7 +53,7 @@ export const AppContextProvider = ({ children, baseURL }: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContext.Provider value={{ get, patch, post, create, destroy }}>
|
<AppContext.Provider value={{ get, patch, post, create, destroy, baseURL }}>
|
||||||
{children}
|
{children}
|
||||||
</AppContext.Provider>
|
</AppContext.Provider>
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,8 @@ import { message } from 'antd'
|
||||||
import { useHistory } from 'react-router'
|
import { useHistory } from 'react-router'
|
||||||
import { User } from '../types'
|
import { User } from '../types'
|
||||||
import { useAppContext } from './AppContext'
|
import { useAppContext } from './AppContext'
|
||||||
import { logOut } from '../api'
|
import { useEffect } from 'react'
|
||||||
|
import { useRef } from 'react'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
|
@ -18,14 +19,36 @@ type Context = {
|
||||||
const UserContext = createContext<Context | null>(null)
|
const UserContext = createContext<Context | null>(null)
|
||||||
|
|
||||||
export const UserContextProvider = ({ children }: Props) => {
|
export const UserContextProvider = ({ children }: Props) => {
|
||||||
const api = useAppContext()
|
const api = useRef(useAppContext())
|
||||||
const history = useHistory()
|
const history = useRef(useHistory())
|
||||||
|
|
||||||
const [user, setUser] = useState<User | null>(null)
|
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) => {
|
const handleLogin = async (username: string, password: string) => {
|
||||||
try {
|
try {
|
||||||
const { key } = await api.post<any, { key: string }>(
|
const { key } = await api.current.post<any, { key: string }>(
|
||||||
'/dj-rest-auth/login/',
|
'/dj-rest-auth/login/',
|
||||||
{
|
{
|
||||||
username,
|
username,
|
||||||
|
@ -34,24 +57,18 @@ export const UserContextProvider = ({ children }: Props) => {
|
||||||
)
|
)
|
||||||
if (!key) throw new Error('Problem logging in!')
|
if (!key) throw new Error('Problem logging in!')
|
||||||
window.localStorage.setItem('cash-stacks-token', key)
|
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)
|
console.log('user', user)
|
||||||
|
|
||||||
if (!user) message.error(`Couldn't find user`)
|
|
||||||
setUser(user)
|
setUser(user)
|
||||||
|
|
||||||
message.success(`logged in as ${user?.name}`, 0.5)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
message.error('Login Failed!')
|
message.error('Login Failed!')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
await logOut()
|
await api.current.post('/dj-rest-auth/logout/', {})
|
||||||
setUser(null)
|
setUser(null)
|
||||||
message.success('logged out!', 0.5)
|
history.current.push('/')
|
||||||
history.push('/')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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) => {
|
export const AccountForm = ({ account }: Props) => {
|
||||||
const stacks = useStacks('')
|
const stacks = useStacks()
|
||||||
|
|
||||||
const [name, setName] = useState<string>(account?.name || '')
|
const [name, setName] = useState<string>(account?.name || '')
|
||||||
const [details, setDetails] = useState<string>(account?.details || '')
|
const [details, setDetails] = useState<string>(account?.details || '')
|
||||||
|
|
|
@ -1,88 +1,39 @@
|
||||||
import { useState, useEffect } from 'react'
|
import { User } from '../types'
|
||||||
|
|
||||||
import { Password, User } from '../types'
|
|
||||||
import { useUserContext } from '../contexts/UserContext'
|
import { useUserContext } from '../contexts/UserContext'
|
||||||
import { message } from 'antd'
|
|
||||||
import { Button } from '../elements/Button'
|
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 = () => {
|
export const UserForm = () => {
|
||||||
const { user } = useUserContext()
|
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 handleFinish = (values: User) => {
|
||||||
const [email, setEmail] = useState(user?.email)
|
console.log(values)
|
||||||
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')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1>{user?.id ? 'Edit' : 'Create'} Profile</h1>
|
<Form form={form} onFinish={handleFinish} initialValues={user}>
|
||||||
<form className="dank-form" onSubmit={handleSubmit}>
|
<h1>User Profile</h1>
|
||||||
<label>
|
<FormItem label="Username" name="username">
|
||||||
Name: <input value={name} onChange={(e) => setName(e.target.value)} />
|
<Input />
|
||||||
</label>
|
</FormItem>
|
||||||
<label>
|
<FormItem label="First Name" name="first_name">
|
||||||
Email:
|
<Input />
|
||||||
<input
|
</FormItem>
|
||||||
value={email}
|
<FormItem label="Last Name" name="last_name">
|
||||||
type="email"
|
<Input />
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
</FormItem>
|
||||||
/>
|
<FormItem label="E-Mail" name="email">
|
||||||
</label>
|
<Input type="email" />
|
||||||
<label>
|
</FormItem>
|
||||||
Password:
|
<Button htmlType="submit">Save</Button>
|
||||||
<input
|
</Form>
|
||||||
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>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Account } from '../../types'
|
import { Account } from '../../types'
|
||||||
import { useGet } from '../util/useGet'
|
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 { Stack } from '../../types'
|
||||||
import { useGet } from '../util/useGet'
|
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'
|
import { useGet } from '../util/useGet'
|
||||||
|
|
||||||
export const useTransactions = (accountId: string) =>
|
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 get = useCallback(async () => {
|
||||||
const data = await appContext.current.get<T>(path)
|
const data = await appContext.current.get<T>(path)
|
||||||
if (!data) return
|
if (!data) return
|
||||||
|
// @ts-ignore
|
||||||
setData(data)
|
setData(data.results)
|
||||||
}, [path])
|
}, [path])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ export const AppFooter = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer>
|
<footer>
|
||||||
<p>User: {user?.name}</p>
|
<p>User: {user?.username}</p>
|
||||||
<p>|</p>
|
<p>|</p>
|
||||||
<p>Budget:</p>
|
<p>Budget:</p>
|
||||||
<p>|</p>
|
<p>|</p>
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { ReactNode } from 'react'
|
||||||
import { Avatar, Dropdown, Menu } from 'antd'
|
import { Avatar, Dropdown, Menu } from 'antd'
|
||||||
import { Link, useHistory } from 'react-router-dom'
|
import { Link, useHistory } from 'react-router-dom'
|
||||||
import { useUserContext } from '../../contexts/UserContext'
|
import { useUserContext } from '../../contexts/UserContext'
|
||||||
|
import { Button } from '../../elements/Button'
|
||||||
|
|
||||||
import './style.scss'
|
import './style.scss'
|
||||||
import { Button } from '../../elements/Button'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
|
@ -27,30 +27,31 @@ export const AppHeader = ({ children }: Props) => {
|
||||||
<header>
|
<header>
|
||||||
<div className="header-left">
|
<div className="header-left">
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<h3>MVP Django React! 🤠</h3>
|
<h3>CashStacks! 💵</h3>
|
||||||
</Link>
|
</Link>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{/* <Button onClick={() => history.push('/new/user')}>New User</Button> */}
|
|
||||||
</div>
|
|
||||||
<div className="header-right">
|
|
||||||
<p>Welcome, {user.name}!!</p>
|
|
||||||
<Dropdown
|
|
||||||
overlay={
|
|
||||||
<Menu style={{ display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<Button onClick={() => history.push('/login')}>Login</Button>
|
|
||||||
<Button onClick={handleLogout}>Logout</Button>
|
|
||||||
<Button onClick={() => history.push('/forgot-password')}>
|
|
||||||
Forgot Password
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => history.push('/profile')}>Profile</Button>
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Avatar>{user.name[0]}</Avatar>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
</div>
|
||||||
|
<nav>{children}</nav>
|
||||||
|
{user && (
|
||||||
|
<div className="header-right">
|
||||||
|
<p>Welcome, {user?.username}!!</p>
|
||||||
|
{/* <Dropdown
|
||||||
|
overlay={
|
||||||
|
<Menu style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Button onClick={() => history.push('/login')}>Login</Button>
|
||||||
|
<Button onClick={handleLogout}>Logout</Button>
|
||||||
|
<Button onClick={() => history.push('/forgot-password')}>
|
||||||
|
Forgot Password
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => history.push('/profile')}>
|
||||||
|
Profile
|
||||||
|
</Button>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Avatar>{user.username[0]}</Avatar>
|
||||||
|
</Dropdown> */}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,23 @@
|
||||||
|
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0.5rem;
|
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width: 100%;
|
padding: 0.5rem;
|
||||||
background: $color-dark;
|
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 {
|
h3 {
|
||||||
color: $color-light;
|
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'
|
import { useStacks } from '../../hooks/getMany/useStacks'
|
||||||
|
|
||||||
export const Dashboard = () => {
|
export const Dashboard = () => {
|
||||||
const stacks = useStacks('42')
|
const stacks = useStacks()
|
||||||
|
|
||||||
if (!stacks.data) return <div>loading...</div>
|
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 { Input, message } from 'antd'
|
||||||
import { useForm } from 'antd/lib/form/Form'
|
import { useForm } from 'antd/lib/form/Form'
|
||||||
import FormItem from 'antd/lib/form/FormItem'
|
import FormItem from 'antd/lib/form/FormItem'
|
||||||
|
import axios from 'axios'
|
||||||
import { useAppContext } from '../../contexts/AppContext'
|
import { useAppContext } from '../../contexts/AppContext'
|
||||||
import { Button } from '../../elements/Button'
|
import { Button } from '../../elements/Button'
|
||||||
import { Form } from '../../elements/Form'
|
import { Form } from '../../elements/Form'
|
||||||
import { User } from '../../types'
|
|
||||||
|
|
||||||
type NewUserForm = Omit<User, 'id'> & {
|
type NewUserForm = {
|
||||||
|
username: string
|
||||||
|
email: string
|
||||||
password1: string
|
password1: string
|
||||||
password2: string
|
password2: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NewUser = () => {
|
export const NewUser = () => {
|
||||||
const api = useAppContext()
|
const { baseURL } = useAppContext()
|
||||||
const [form] = useForm<NewUserForm>()
|
const [form] = useForm<NewUserForm>()
|
||||||
|
|
||||||
const handleFinish = (user: NewUserForm) => {
|
const handleFinish = (user: NewUserForm) => {
|
||||||
|
@ -21,7 +23,7 @@ export const NewUser = () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
api.post(`/dj-rest-auth/registration`, user)
|
axios.post(`${baseURL}/dj-rest-auth/registration/`, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,11 +31,11 @@ export const NewUser = () => {
|
||||||
<FormItem label="username" name="username">
|
<FormItem label="username" name="username">
|
||||||
<Input></Input>
|
<Input></Input>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="email" name="email">
|
{/* <FormItem label="email" name="email">
|
||||||
<Input type="email"></Input>
|
<Input type="email"></Input>
|
||||||
</FormItem>
|
</FormItem> */}
|
||||||
<FormItem label="password" name="password1">
|
<FormItem label="password" name="password1">
|
||||||
<Input minLength={8}></Input>
|
<Input minLength={6}></Input>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="confirm" name="password2">
|
<FormItem label="confirm" name="password2">
|
||||||
<Input></Input>
|
<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 = {
|
export type User = {
|
||||||
id: string
|
id: number
|
||||||
name: string
|
username: string
|
||||||
email: string
|
email: string
|
||||||
// password: string
|
first_name: string
|
||||||
|
last_name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Account = {
|
export type Account = {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user