almost mvp

This commit is contained in:
Elijah Lucian 2021-07-16 22:07:41 -06:00
parent 674e3fd253
commit 2ebe8bcd1f
17 changed files with 130 additions and 68 deletions

View File

@ -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>

View File

@ -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!')

View File

@ -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' })

View File

@ -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' })

View File

@ -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/`)

View File

@ -0,0 +1,4 @@
import { Account } from '../../types'
import { useGet } from '../util/useGet'
export const useAccount = (id: string) => useGet<Account>(`/accounts/${id}/`)

View File

@ -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
if (config?.mode === 'list') {
// @ts-ignore
setData(data.results)
}, [path])
} else {
setData(data)
}
}, [path, config])
useEffect(() => {
get()

View 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>
}

View File

@ -0,0 +1,3 @@
.dank-page {
padding: 1rem;
}

View File

@ -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>
)
}

View File

@ -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>
{accounts.data?.map((account) => (
<div className="account-overview">
<h3>{account.name}</h3>
<div className="funds">
{stacks.data.map((stack, i) => (
<FundBar key={stack.id} stack={stack} col={i + 1} />
{account.stacks.map((stack) => (
<FundBar key={stack.id} stack={stack} />
))}
</div>
</>
</div>
))}
</Page>
)
}

View 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;
}
}

View File

@ -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>
)
}

View File

@ -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;
}
}

View File

@ -20,6 +20,7 @@ export type Account = {
details: string
income: number
expenses: number
stacks: Stack[]
}
export type Stack = {

View File

@ -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>
)
}

View File

@ -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;
}
}