almost mvp
This commit is contained in:
parent
674e3fd253
commit
2ebe8bcd1f
|
@ -1,6 +1,6 @@
|
|||
import { useUserContext } from './contexts/UserContext'
|
||||
import { Route, Switch } from 'react-router'
|
||||
import { Link, useHistory } from 'react-router-dom'
|
||||
import { Link, Redirect, useHistory } from 'react-router-dom'
|
||||
import { Dashboard } from './pages/Dashboard'
|
||||
import { NewUser } from './pages/NewUser'
|
||||
import { TransactionList } from './pages/TransactionList'
|
||||
|
@ -38,7 +38,8 @@ export const MainLayout = () => {
|
|||
<Route path="/transactions" component={TransactionList} />
|
||||
<Route path="/accounts/new" component={AccountForm} />
|
||||
<Route path="/accounts" component={AccountList} />
|
||||
<Route path="/" component={Dashboard} />
|
||||
<Route path="/dashboard" component={Dashboard} />
|
||||
<Redirect to="/dashboard" />
|
||||
</Switch>
|
||||
)}
|
||||
</main>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { User } from '../types'
|
|||
import { useAppContext } from './AppContext'
|
||||
import { useEffect } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import axios from 'axios'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
|
@ -48,17 +49,18 @@ export const UserContextProvider = ({ children }: Props) => {
|
|||
|
||||
const handleLogin = async (username: string, password: string) => {
|
||||
try {
|
||||
const { key } = await api.current.post<any, { key: string }>(
|
||||
'/dj-rest-auth/login/',
|
||||
const res = await axios.post<{ key: string }>(
|
||||
'http://localhost:8000/dj-rest-auth/login/',
|
||||
{
|
||||
username,
|
||||
password,
|
||||
},
|
||||
)
|
||||
if (!key) throw new Error('Problem logging in!')
|
||||
window.localStorage.setItem('cash-stacks-token', key)
|
||||
if (!res.data.key) throw new Error('Problem logging in!')
|
||||
|
||||
window.localStorage.setItem('cash-stacks-token', res.data.key)
|
||||
|
||||
const user = await api.current.get<User>(`/dj-rest-auth/user/`)
|
||||
console.log('user', user)
|
||||
setUser(user)
|
||||
} catch (err) {
|
||||
message.error('Login Failed!')
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Account } from '../../types'
|
||||
import { useGet } from '../util/useGet'
|
||||
|
||||
export const useAccounts = () => useGet<Account[]>(`/accounts/`)
|
||||
export const useAccounts = () =>
|
||||
useGet<Account[]>(`/accounts/`, { mode: 'list' })
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Stack } from '../../types'
|
||||
import { useGet } from '../util/useGet'
|
||||
|
||||
export const useStacks = () => useGet<Stack[]>(`/stacks/`)
|
||||
export const useStacks = () => useGet<Stack[]>(`/stacks/`, { mode: 'list' })
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Transaction } from '../../types'
|
||||
import { useGet } from '../util/useGet'
|
||||
|
||||
export const useTransactions = (accountId: string) =>
|
||||
useGet<Transaction[]>(`/transactions/`)
|
||||
export const useTransactions = () => useGet<Transaction[]>(`/transactions/`)
|
||||
|
|
4
frontend/src/hooks/getOne/useAccount.ts
Normal file
4
frontend/src/hooks/getOne/useAccount.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { Account } from '../../types'
|
||||
import { useGet } from '../util/useGet'
|
||||
|
||||
export const useAccount = (id: string) => useGet<Account>(`/accounts/${id}/`)
|
|
@ -1,16 +1,24 @@
|
|||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useAppContext } from '../../contexts/AppContext'
|
||||
|
||||
export const useGet = <T>(path: string) => {
|
||||
type Config = {
|
||||
mode: 'list'
|
||||
}
|
||||
|
||||
export const useGet = <T>(path: string, config?: Config) => {
|
||||
const appContext = useRef(useAppContext())
|
||||
const [data, setData] = useState<T | null>(null)
|
||||
|
||||
const get = useCallback(async () => {
|
||||
const data = await appContext.current.get<T>(path)
|
||||
if (!data) return
|
||||
// @ts-ignore
|
||||
setData(data.results)
|
||||
}, [path])
|
||||
if (config?.mode === 'list') {
|
||||
// @ts-ignore
|
||||
setData(data.results)
|
||||
} else {
|
||||
setData(data)
|
||||
}
|
||||
}, [path, config])
|
||||
|
||||
useEffect(() => {
|
||||
get()
|
||||
|
|
10
frontend/src/layout/Page/index.tsx
Normal file
10
frontend/src/layout/Page/index.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import React from 'react'
|
||||
import './style.scss'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const Page = ({ children }: Props) => {
|
||||
return <div className="dank-page">{children}</div>
|
||||
}
|
3
frontend/src/layout/Page/style.scss
Normal file
3
frontend/src/layout/Page/style.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
.dank-page {
|
||||
padding: 1rem;
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
import { useAccounts } from '../../hooks/getMany/useAccounts'
|
||||
import { Page } from '../../layout/Page'
|
||||
|
||||
export const AccountList = () => {
|
||||
const accounts = useAccounts()
|
||||
|
||||
return <div>Account Count: {accounts.data?.length}</div>
|
||||
return (
|
||||
<Page>
|
||||
<h1>Account List</h1>
|
||||
<p>Count: {accounts.data?.length}</p>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
import { FundBar } from '../../widgets/FundBar'
|
||||
import { useStacks } from '../../hooks/getMany/useStacks'
|
||||
import { Page } from '../../layout/Page'
|
||||
import './style.scss'
|
||||
import { useAccounts } from '../../hooks/getMany/useAccounts'
|
||||
|
||||
export const Dashboard = () => {
|
||||
const stacks = useStacks()
|
||||
|
||||
if (!stacks.data) return <div>loading...</div>
|
||||
const accounts = useAccounts()
|
||||
if (!accounts.data) return <div>loading...</div>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Page>
|
||||
<h1>Remaining Balances</h1>
|
||||
<div className="funds">
|
||||
{stacks.data.map((stack, i) => (
|
||||
<FundBar key={stack.id} stack={stack} col={i + 1} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
{accounts.data?.map((account) => (
|
||||
<div className="account-overview">
|
||||
<h3>{account.name}</h3>
|
||||
<div className="funds">
|
||||
{account.stacks.map((stack) => (
|
||||
<FundBar key={stack.id} stack={stack} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
|
19
frontend/src/pages/Dashboard/style.scss
Normal file
19
frontend/src/pages/Dashboard/style.scss
Normal file
|
@ -0,0 +1,19 @@
|
|||
@import '../../scss/variables.scss';
|
||||
|
||||
.account-overview {
|
||||
display: flex;
|
||||
border: 1px solid $color-grey;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin: 1rem 0;
|
||||
padding: 1rem;
|
||||
background: $color-alt;
|
||||
|
||||
.funds {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
|
@ -1,18 +1,26 @@
|
|||
import { useTransactions } from '../../hooks/getMany/useTransactions'
|
||||
import { Page } from '../../layout/Page'
|
||||
import './style.scss'
|
||||
|
||||
export const TransactionList = () => {
|
||||
const transactions = useTransactions()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Page>
|
||||
<h1>Transactions</h1>
|
||||
|
||||
<div className="transactions">
|
||||
{[].map((account, i) => {
|
||||
{transactions.data?.map((tx, i) => {
|
||||
return (
|
||||
<div className="column" key={`${account}-txs-${i}`}>
|
||||
<h3>{account}</h3>
|
||||
<div className="dank-transaction" key={`${tx.id}-txs-${i}`}>
|
||||
<p>
|
||||
{tx.details}: ${tx.amount}
|
||||
</p>
|
||||
<p>on: {tx.created_at}</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,21 +1,10 @@
|
|||
.transactions {
|
||||
.column {
|
||||
h3 {
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
}
|
||||
margin: 2ch;
|
||||
background: #fff1;
|
||||
padding: 0 2ch 1ch;
|
||||
border-radius: 1ch;
|
||||
}
|
||||
@import '../../scss/variables.scss';
|
||||
|
||||
.transaction-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
.details {
|
||||
padding-left: 1ch;
|
||||
}
|
||||
.transactions {
|
||||
.dank-transaction {
|
||||
border: 1px solid $color-dark;
|
||||
margin: 1rem 0;
|
||||
background: #fff1;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ export type Account = {
|
|||
details: string
|
||||
income: number
|
||||
expenses: number
|
||||
stacks: Stack[]
|
||||
}
|
||||
|
||||
export type Stack = {
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import { Stack } from '../../types'
|
||||
import './style.scss'
|
||||
import _ from 'lodash'
|
||||
|
||||
type Props = {
|
||||
col: number
|
||||
stack: Stack
|
||||
}
|
||||
|
||||
export const FundBar = ({ stack, col }: Props) => {
|
||||
const amount = 0
|
||||
|
||||
export const FundBar = ({ stack }: Props) => {
|
||||
const amount = _.sumBy(stack.transactions, 'amount')
|
||||
console.log('amount', stack.id, amount)
|
||||
const max = stack.amount
|
||||
const current = max - amount
|
||||
const percent = Math.max(current / max, 0)
|
||||
const hue = percent * 120
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="fundbar">
|
||||
<div
|
||||
className={`fundbar back col${col}`}
|
||||
className={`back`}
|
||||
onClick={() => console.log(`adding transaction to => ${stack.name}`)}
|
||||
></div>
|
||||
<div
|
||||
className={`fundbar front col${col}`}
|
||||
className={`front`}
|
||||
style={{
|
||||
background: `hsl(${hue}, 100%, 50%)`,
|
||||
height: `${percent * 40 + 10}vmin`,
|
||||
|
@ -30,11 +30,11 @@ export const FundBar = ({ stack, col }: Props) => {
|
|||
<h3>{stack.name}</h3>
|
||||
</div>
|
||||
<div
|
||||
className={`fundbar totals col${col}`}
|
||||
className={`totals`}
|
||||
style={{ color: `hsl(0, 0%, ${Math.abs(1 - percent) * 100}%)` }}
|
||||
>
|
||||
${Math.floor(current)} / ${max}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,32 +1,37 @@
|
|||
@import '../../scss/variables.scss';
|
||||
|
||||
.fundbar {
|
||||
cursor: pointer;
|
||||
border-radius: 1ch;
|
||||
text-align: center;
|
||||
margin: auto 2ch 0 2ch;
|
||||
margin: 1rem;
|
||||
display: grid;
|
||||
grid-template: 1fr / 1fr;
|
||||
|
||||
&.totals {
|
||||
.totals {
|
||||
pointer-events: none;
|
||||
margin-top: 2ch;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: auto auto 0 auto;
|
||||
}
|
||||
|
||||
&.front {
|
||||
.front {
|
||||
grid-area: 1/1/2/2;
|
||||
border-radius: 1ch;
|
||||
pointer-events: none;
|
||||
transition: all 0.2s ease-out;
|
||||
border: 2px solid #222a;
|
||||
border: 2px solid $color-dark;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2ch;
|
||||
text-shadow: 2px 2px #2223, -1px -1px #fffa;
|
||||
}
|
||||
|
||||
&.back {
|
||||
.back {
|
||||
grid-area: 1/1/2/2;
|
||||
background: #222;
|
||||
|
||||
border-radius: 1ch;
|
||||
border: 4px solid #3334;
|
||||
height: 50vh;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user