stuff, with a side of things

This commit is contained in:
Elijah Lucian 2021-07-15 13:51:57 -06:00
parent c62f88953e
commit 8e7fc05cd1
20 changed files with 21741 additions and 230 deletions

21663
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,11 +6,13 @@
"@dank-inc/data-buddy": "^0.2.0",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/lodash": "^4.14.171",
"@types/node": "^12.0.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"antd": "^4.14.0",
"axios": "^0.21.1",
"lodash": "^4.17.21",
"node-sass": "^4.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",

View File

@ -8,7 +8,7 @@ import './app.scss'
const App = () => {
return (
<BrowserRouter>
<AppContextProvider>
<AppContextProvider baseURL="/api">
<UserContextProvider>
<CoreLayout />
</UserContextProvider>

View File

@ -1,49 +0,0 @@
import { DataBuddy } from '@dank-inc/data-buddy'
import { Account, Stack, Transaction, User } from '../../types'
export const users = new DataBuddy<User>([
{
id: 'mock-user',
name: 'TestUser42',
email: 'testuser@email.com',
},
])
export const accounts = new DataBuddy<Account>([
{
id: 'home',
name: 'Home Expenses',
details: 'ya',
users: ['42'],
income: 1000,
expenses: 500,
},
])
export const stacks = new DataBuddy<Stack>([
{
id: 'ccrap',
name: 'crap',
account: 'asdf',
amount: 200,
details: 'for all my crap!',
transactions: [],
},
{
id: 'shit',
name: 'shit',
account: 'home',
amount: 500,
details: 'for all my shit!',
transactions: [],
},
{
id: 'poo',
name: 'poo',
account: 'home',
amount: 800,
details: 'for all my poo!',
transactions: [],
},
])
export const transactions = new DataBuddy<Transaction>([])

View File

@ -1,8 +1,7 @@
import Axios, { AxiosInstance } from 'axios'
import { Account, Password, Stack, Transaction, User, uuid } from '../types'
import { Account, Password, Stack, Transaction, User } from '../types'
import { JWT, setJWT, wipeJWT } from '../utils/jwt'
import { DataBuddy } from '@dank-inc/data-buddy'
import { users, accounts, stacks, transactions } from './data'
export type ApiParams = {
baseURL?: string
@ -21,24 +20,10 @@ export interface Api {
export class Api {
constructor({ mock, baseURL }: ApiParams) {
this.mock = mock
this.users = users
this.accounts = accounts
this.stacks = stacks
this.transactions = transactions
this.axios = Axios.create({ baseURL })
}
login = async (name: string, password: string): Promise<JWT> => {
if (this.mock) {
const jwt = {
id: 'mock-user',
token: 'token-token-token',
exp: +new Date(),
}
setJWT(jwt)
return jwt
}
const { data } = await this.axios.post<JWT>(`/dj-rest-auth/login/`, {
name,
password,
@ -49,18 +34,15 @@ export class Api {
}
logout = () => {
if (this.mock) return true
wipeJWT()
}
getUser = async (id: uuid) => {
if (this.mock) return this.users.getOne(id)
getUser = async (id: string) => {
const { data } = await this.axios.get<User>(`users/${id}`)
return data
}
updateUser = async (id: uuid, body: Partial<User>) => {
if (this.mock) return this.users.update(id, body)
updateUser = async (id: string, body: Partial<User>) => {
const { data } = await this.axios.patch<User>(`users/${id}`, body)
return data
}
@ -71,17 +53,15 @@ export class Api {
}
getAccounts = async () => {
if (this.mock) return this.accounts.get()
const { data } = await this.axios.get<Account[]>('accounts')
return data
}
getAccount = async (id: uuid) => {
if (this.mock) return this.accounts.getOne(id)
getAccount = async (id: string) => {
const data = await this.axios.get<Account>(`accounts/${id}`)
return data
}
updateAccount = async (id: uuid, body: Partial<Account>) => {
updateAccount = async (id: string, body: Partial<Account>) => {
const { data } = await this.axios.patch<Account>(`accounts/${id}`, body)
return data
}
@ -92,12 +72,11 @@ export class Api {
deleteAccount = async () => {}
getStacks = async (): Promise<Stack[]> => {
if (this.mock) return this.stacks.get()
const { data } = await this.axios.get('stacks')
return data
}
updateStack = async (id: uuid, body: Partial<Stack>) => {
updateStack = async (id: string, body: Partial<Stack>) => {
const { data } = await this.axios.patch<Stack>(`stacks/${id}`, body)
return data
}
@ -106,12 +85,11 @@ export class Api {
deleteStack = async () => {}
getTransactions = async () => {
if (this.mock) return this.transactions.get()
const { data } = await this.axios.get('transactions')
return data
}
updateTransaction = async (id: uuid, body: Partial<Transaction>) => {
updateTransaction = async (id: string, body: Partial<Transaction>) => {
const { data } = await this.axios.patch<Transaction>(
`transactions/${id}`,
body,

View File

@ -1,4 +1,4 @@
import Axios, { AxiosResponse } from 'axios'
import axios, { AxiosResponse } from 'axios'
import React, { createContext, useContext } from 'react'
type Props = {
@ -6,7 +6,7 @@ type Props = {
baseURL?: string
}
type Context = {
type AppContextInterface = {
get: <T>(path: string) => Promise<T>
patch: <T>(path: string, body: Partial<T>) => Promise<T>
post: <T, R = T>(path: string, body: Partial<T>) => Promise<R>
@ -14,30 +14,35 @@ type Context = {
destroy: (path: string) => Promise<string>
}
const AppContext = createContext<Context | null>(null)
const AppContext = createContext<AppContextInterface | null>(null)
export const AppContextProvider = ({ children, baseURL }: Props) => {
const axios = Axios.create({ baseURL })
const api = axios.create({ baseURL })
api.interceptors.request.use((config) => {
config.url += '?format=json'
return config
})
async function get<T>(path: string): Promise<T> {
const res = await axios.get<T>(path)
const res = await api.get<T>(path)
return res.data
}
async function patch<T>(path: string, body: Partial<T>) {
const res = await axios.patch<T>(path, body)
const res = await api.patch<T>(path, body)
return res.data
}
async function post<T, R = T>(path: string, body: Partial<T>) {
const res = await axios.post<T, AxiosResponse<R>>(path, body)
const res = await api.post<T, AxiosResponse<R>>(path, body)
return res.data
}
async function create<T>(path: string, body: Partial<T>) {
// unauthed POST
const res = await axios.post<T>(path, body)
const res = await api.post<T>(path, body)
return res.data
}
async function destroy(path: string) {
const res = await axios.delete<string>(path)
const res = await api.delete<string>(path)
return res.data
}

View File

@ -1,7 +1,7 @@
import React, { createContext, useContext, useState } from 'react'
import { message } from 'antd'
import { useHistory } from 'react-router'
import { Account, User, uuid } from '../types'
import { User } from '../types'
import { useAppContext } from './AppContext'
import { logOut } from '../api'
@ -11,11 +11,8 @@ type Props = {
type Context = {
user: User | null
accounts: Account[] | null
selectedAccount: Account | null
handleLogin: (name: string, password: string) => void
handleLogout: () => void
handleSelectAccount: (id: uuid) => void
}
const UserContext = createContext<Context | null>(null)
@ -25,10 +22,10 @@ export const UserContextProvider = ({ children }: Props) => {
const history = useHistory()
const [user, setUser] = useState<User | null>(null)
const [accounts, setAccounts] = useState<Account[] | null>(null)
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null)
const handleLogin = async (name: string, password: string) => {
console.log('logging in?', name, password)
try {
const { id } = await api.post<any, { id: string }>('/login', {
name,
@ -40,9 +37,6 @@ export const UserContextProvider = ({ children }: Props) => {
if (!user) message.error(`Couldn't find user`)
setUser(user)
const accounts = await api.get<Account[]>('/accounts')
setAccounts(accounts)
message.success(`logged in as ${user?.name}`, 0.5)
} catch (err) {
message.error('Login Failed!')
@ -56,20 +50,12 @@ export const UserContextProvider = ({ children }: Props) => {
history.push('/')
}
const handleSelectAccount = (id: string) => {
const account = accounts?.find((account) => account.id === id)
if (account) setSelectedAccount(account)
}
return (
<UserContext.Provider
value={{
user,
accounts,
handleLogin,
handleLogout,
selectedAccount,
handleSelectAccount,
}}
>
{children}

View File

@ -1,12 +1,11 @@
import { Button as AntButton, ButtonProps } from 'antd'
import React from 'react'
import './style.scss'
type Props = ButtonProps
export const Button = ({ children, htmlType }: Props) => {
export const Button = ({ children, ...props }: Props) => {
return (
<AntButton className="dank-button" htmlType={htmlType}>
<AntButton className="dank-button" {...props}>
{children}
</AntButton>
)

View File

@ -3,6 +3,10 @@ import './style.scss'
type Props = FormProps
export const Form = ({ children }: Props) => {
return <AntForm className="dank-form">{children}</AntForm>
export const Form = ({ children, ...props }: Props) => {
return (
<AntForm className="dank-form" {...props}>
{children}
</AntForm>
)
}

View File

@ -1,5 +1,4 @@
import { Account } from '../../types'
import { useGet } from '../util/useGet'
export const useAccounts = (userId: string) =>
useGet<Account[]>(`/users/${userId}/accounts`)
export const useAccounts = () => useGet<Account[]>(`/accounts`)

View File

@ -1,5 +1,4 @@
import { Stack } from '../../types'
import { useGet } from '../util/useGet'
export const useStacks = (accountId: string) =>
useGet<Stack[]>(`/accounts/${accountId}/stacks`)
export const useStacks = (accountId: string) => useGet<Stack[]>(`/stacks`)

View File

@ -1,21 +1,15 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { useAppContext } from '../../contexts/AppContext'
type Res<T> = {
data: T
}
export const useGet = <T>(path: string) => {
const appContext = useRef(useAppContext())
const [data, setData] = useState<T | null>(null)
const get = useCallback(async () => {
// if (appContext.current.mock) return setData(mockGet[path]?.() || null)
const data = await appContext.current.get<Res<T>>(path)
const data = await appContext.current.get<T>(path)
if (!data) return
setData(data.data)
setData(data)
}, [path])
useEffect(() => {

View File

@ -2,7 +2,7 @@ import { FundBar } from '../../widgets/FundBar'
import { useStacks } from '../../hooks/getMany/useStacks'
export const Dashboard = () => {
const stacks = useStacks('')
const stacks = useStacks('42')
if (!stacks.data) return <div>loading...</div>

View File

@ -1,28 +1,33 @@
import { Input } from 'antd'
import FormItem from 'antd/lib/form/FormItem'
import { Link } from 'react-router-dom'
import { User } from '../../types'
import { useUserContext } from '../../contexts/UserContext'
import { Form } from '../../elements/Form'
import { Button } from '../../elements/Button'
import { useForm } from 'antd/lib/form/Form'
import { useAppContext } from '../../contexts/AppContext'
import { useStacks } from '../../hooks/getMany/useStacks'
type FormValues = Pick<User, 'name'> & { password: string }
type FormValues = { email: string; password: string }
export const Login = () => {
const userContext = useUserContext()
const appContext = useAppContext()
const stacks = useStacks('')
const [form] = useForm<FormValues>()
const handleFinish = ({ name, password }: FormValues) => {
userContext.handleLogin(name, password)
const handleFinish = async ({ email, password }: FormValues) => {
const res = await appContext.post('/dj-rest-auth/login', {
email,
password,
})
console.log(res)
}
return (
<div className="login">
<Form onFinish={handleFinish} form={form}>
<h1>Log In</h1>
<FormItem label="Username" name="username">
<FormItem label="email" name="email">
<Input />
</FormItem>
<FormItem label="Password" name="password">

View File

@ -1,32 +0,0 @@
import { Button, Form, Input } from 'antd'
import { useForm } from 'antd/lib/form/Form'
import { useUserContext } from '../../contexts/UserContext'
import './style.scss'
type Credentials = {
username: string
password: string
}
export const Login = () => {
const { handleLogin } = useUserContext()
const [form] = useForm<Credentials>()
const handleSubmit = ({ username, password }: Credentials) => {
handleLogin(username, password)
}
return (
<Form form={form} onFinish={handleSubmit}>
<Form.Item label="username" name="username">
<Input />
</Form.Item>
<Form.Item label="password" name="password">
<Input type="password" />
</Form.Item>
<Button type="primary" htmlType="submit">
Login!
</Button>
</Form>
)
}

View File

@ -11,7 +11,6 @@ export const NewUser = () => {
const [form] = Form.useForm<NewUserForm>()
const handleFinish = (user: NewUserForm) => {
console.log('Asdfdas')
if (user.password1 !== user.password2) {
message.error('passwords do not match')
return
@ -20,29 +19,23 @@ export const NewUser = () => {
axios.post(`/dj-rest-auth/registration/`, user)
}
console.log('ASDFUASDJF')
return (
<Layout>
<Layout.Content>
<Form form={form} onFinish={handleFinish}>
<Form.Item label="username" name="username">
<Input></Input>
</Form.Item>
<Form.Item label="email" name="email">
<Input type="email"></Input>
</Form.Item>
<Form.Item label="password" name="password1">
<Input minLength={8}></Input>
</Form.Item>
<Form.Item label="confirm" name="password2">
<Input></Input>
</Form.Item>
<Button type="primary" htmlType="submit">
Create
</Button>
</Form>
</Layout.Content>
</Layout>
<Form form={form} onFinish={handleFinish}>
<Form.Item label="username" name="username">
<Input></Input>
</Form.Item>
<Form.Item label="email" name="email">
<Input type="email"></Input>
</Form.Item>
<Form.Item label="password" name="password1">
<Input minLength={8}></Input>
</Form.Item>
<Form.Item label="confirm" name="password2">
<Input></Input>
</Form.Item>
<Button type="primary" htmlType="submit">
Create
</Button>
</Form>
)
}

View File

@ -1,15 +1,13 @@
export type uuid = string
export type User = {
id: uuid
id: string
name: string
email: string
// password: string
}
export type Account = {
id: uuid
users?: uuid[]
id: string
users?: string[]
name: string
details: string
income: number
@ -17,8 +15,8 @@ export type Account = {
}
export type Stack = {
id: uuid
account: uuid //'38485982-87f3-4a11-a963-2202983809e3'
id: string
account: string //'38485982-87f3-4a11-a963-2202983809e3'
name: string // 'House Fund'
details: string //'buy furniture'
amount: number // '200.00'
@ -26,8 +24,8 @@ export type Stack = {
}
export type Transaction = {
id: uuid
stack: uuid // '0058cece-3ff3-4ee1-b71d-075a0bc73bc0'
id: string
stack: string // '0058cece-3ff3-4ee1-b71d-075a0bc73bc0'
details: string // 'by ghetto couch off Kijiji'
amount: number // '30.44'
created_at: string // '2021-04-15T00:02:45.096071Z'

View File

@ -1,16 +1,15 @@
import { Link } from 'react-router-dom'
import { message } from 'antd'
import { useUserContext } from '../../contexts/UserContext'
import './style.scss'
import { Button } from '../../elements/Button'
import { useAccounts } from '../../hooks/getMany/useAccounts'
type Props = {
selectProfile: (id: string) => void
}
export const AccountSelect = ({ selectProfile }: Props) => {
const { accounts } = useUserContext()
const accounts = useAccounts()
const handleSelect = (id: string) => {
selectProfile(id)
@ -21,16 +20,14 @@ export const AccountSelect = ({ selectProfile }: Props) => {
<>
<h1>Select Account</h1>
<div className="account-select">
{accounts?.length
? accounts.map((account) => (
<Button
key={`account-${account.name}`}
onClick={() => handleSelect(account.id)}
>
{account.name}
</Button>
))
: ''}
{accounts.data?.map((account) => (
<Button
key={`account-${account.name}`}
onClick={() => handleSelect(account.id)}
>
{account.name}
</Button>
))}
<Link to="/account/new">Create New Budget!</Link>
</div>
</>

View File

@ -1784,6 +1784,11 @@
"resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
"version" "0.0.29"
"@types/lodash@^4.14.171":
"integrity" "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg=="
"resolved" "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz"
"version" "4.14.171"
"@types/minimatch@*":
"integrity" "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA=="
"resolved" "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz"
@ -4164,11 +4169,6 @@
"resolved" "https://registry.npmjs.org/date-fns/-/date-fns-2.21.0.tgz"
"version" "2.21.0"
"dayjs@^1.8.30":
"integrity" "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw=="
"resolved" "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz"
"version" "1.10.4"
"debug@^2.2.0":
"integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="
"resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"

View File

@ -1,7 +1,9 @@
users:
- name: elijah
password: toffee15
- name: toffee
- name: ievgen
password: toffee15
- name: tanner
password: toffee15
accounts: