This commit is contained in:
cupcakearmy
2019-05-22 20:34:58 +02:00
parent 7b81984ce7
commit dcbc4bcdd7
73 changed files with 4138 additions and 0 deletions

38
www/pages/index.jsx Executable file
View File

@@ -0,0 +1,38 @@
import React from 'react'
import Layout from '../components/layout'
import { callAPI } from '../utils/api'
import { withAuthSync } from '../utils/auth'
import Chart from '../components/chart'
import Purchase from '../components/purchase'
const deletePurchase = id => callAPI(null, {
url: `/api/purchases/${id}`,
method: 'delete',
}).then(() => location.reload()).catch(() => alert('There was a problem deleting the purchase'))
const Home = ({ purchases, stats, me }) => {
return <Layout>
<h3 className={'text-center'}>summa</h3>
<Chart stats={stats}/>
<br/><br/>
<h4 className={'text-center'}>diarium</h4>
<div className={'pl-2'}>
{purchases.map((purchase, i) => <Purchase key={i} {...{ purchase, me }}/>)}
</div>
</Layout>
}
Home.getInitialProps = async ctx => ({
stats: await callAPI(ctx, { url: '/api/purchases/stats' }),
purchases: await callAPI(ctx, { url: `/api/purchases/` }),
me: await callAPI(ctx, { url: '/api/users/me' }),
})
export default withAuthSync(Home)

61
www/pages/login.jsx Executable file
View File

@@ -0,0 +1,61 @@
import React, { useState } from 'react'
import Layout from '../components/layout'
import { login } from '../utils/auth'
import { callAPI } from '../utils/api'
import { capitalize } from '../utils/misc'
const Login = ({ users }) => {
const [user, setUser] = useState(users[0])
const [password, setPassword] = useState('')
const [error, setError] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
callAPI(null, {
method: 'post',
url: '/api/users/login',
data: { user, password },
})
.then(login)
.catch(() => setError(true))
}
return <Layout>
<form onSubmit={handleSubmit}>
<div className={`form-group ${error ? 'has-error' : ''}`}>
<select className="form-select"
name='username'
placeholder="username"
value={user}
onChange={e => setUser(e.target.value)}
>
{users.map((username, i) => <option key={i} value={username}>{capitalize(username)}</option>)}
</select>
<label className="form-label" htmlFor="input-example-1">Password</label>
<input
className="form-input"
type='password'
placeholder='password'
value={password}
onChange={e => setPassword(e.target.value)}
/>
<br/>
<button type='submit' className={'btn btn-primary'}>Login</button>
{error && <React.Fragment><br/><p className="form-input-hint">Unauthorized</p></React.Fragment>}
</div>
</form>
</Layout>
}
Login.getInitialProps = async ctx => ({
users: await callAPI(ctx, { url: `/api/users/names` }),
})
export default Login

65
www/pages/me.jsx Executable file
View File

@@ -0,0 +1,65 @@
import React from 'react'
import Layout from '../components/layout'
import { callAPI } from '../utils/api'
import { capitalize, getAvatarOfFallback } from '../utils/misc'
import { withAuthSync } from '../utils/auth'
const avatars = ['anteater', 'bear', 'beaver', 'boar', 'buffalo-1', 'buffalo', 'cat', 'chicken', 'cow', 'crow', 'dog-1', 'dog', 'donkey', 'elephant', 'fox', 'giraffe', 'hedgehog', 'hen', 'hippopotamus', 'horse', 'kangaroo', 'koala', 'leopard', 'lion', 'marten', 'monkey-1', 'monkey', 'mouse', 'octopus', 'ostrich', 'owl', 'panda', 'parrot', 'penguin-1', 'penguin', 'pig', 'polar-bear', 'rabbit', 'racoon', 'rhinoceros', 'rooster', 'seagull', 'seal', 'sheep-1', 'sheep', 'sloth', 'snake', 'tiger', 'whale', 'zebra']
const selectAvatar = avatar => callAPI(null, {
url: `/api/users/me/avatar`,
method: 'post',
data: { avatar },
}).then(() => location.reload()).catch(() => alert('There was a problem deleting the purchase'))
const Me = me => {
const { name, debts, purchases, avatar } = me
return <Layout>
<h1>{capitalize(name)}</h1>
<h5>Current Avatar</h5>
<img alt="avatar-icon" src={`/static/icons/animals/${getAvatarOfFallback(avatar)}.svg`}
className="avatar avatar-xl"/>
<br/><br/>
<h5>Select Avatar</h5>
<div className={'selector'}>
{avatars.map((avatar, i) => <img
onClick={() => selectAvatar(avatar)}
key={i} alt="avatar-icon"
src={`/static/icons/animals/${avatar}.svg`}
className="avatar avatar-md m-1"
/>)}
</div>
{/*<br/><br/>*/}
{/*<h2>Purchases</h2>*/}
{/*{purchases.map((purchase, i) => <Purchase key={i} {...{ purchase, me }} />)}*/}
{/*<br/><br/>*/}
{/*<h2>Debts</h2>*/}
{/*{debts.map((purchase, i) => <Purchase key={i} {...{ purchase, me }} />)}*/}
{/* language=CSS */}
<style jsx>{`
.selector img {
cursor: pointer;
transition: var(--animation);
}
.selector img:hover {
transform: scale(1.1);
}
`}</style>
</Layout>
}
Me.getInitialProps = async ctx => await callAPI(ctx, { url: `/api/users/me` })
export default withAuthSync(Me)

139
www/pages/new.jsx Normal file
View File

@@ -0,0 +1,139 @@
import React, { useRef, useState } from 'react'
import Router from 'next/router'
import Layout from '../components/layout'
import { withAuthSync } from '../utils/auth'
import { callAPI } from '../utils/api'
const Profile = props => {
const { users } = props
const check = useRef(undefined)
const [photo, setPhoto] = useState(undefined)
const [price, setPrice] = useState('')
const [description, setDescription] = useState('')
const [debtors, setDebtors] = useState(users.reduce((acc, users) => {
acc[users.name] = true
return acc
}, {}))
const removeUpload = () => {
setPhoto(false)
}
const handlePhoto = e => {
const reader = new FileReader()
reader.onload = e => {
const converted = Buffer.from(e.target.result).toString('base64')
check.current.src = `data:image/jpeg;base64,${converted}`
setPhoto(converted)
}
reader.readAsArrayBuffer(e.target.files[0])
}
const submit = async (e) => {
e.preventDefault()
console.log(e.files)
const selectedDebtors = Object.entries(debtors)
.filter(([name, selected]) => selected)
.map(([name, selected]) => name)
await callAPI(null, {
url: `/api/purchases`,
method: 'post',
data: {
price: parseFloat(price),
debtors: selectedDebtors,
description,
},
})
Router.push('/')
}
return <Layout>
<h1>Add New</h1>
<form onSubmit={submit}>
<div className="form-group">
<label className="form-label">Price</label>
<input
className="form-input"
placeholder={'Price'}
value={price}
onChange={e => setPrice(e.target.value)}
type={'number'} min={0} step={0.01}
/>
<br/>
<input
className="form-input"
placeholder={'I haz bought...'}
value={description}
onChange={e => setDescription(e.target.value)}
/>
<br/>
{photo
? <button onClick={removeUpload} type={'button'} className="btn btn-primary">Delete Photo</button>
: <div className="fileUpload btn btn-primary">
<span>Upload a Photo</span>
<input
type="file"
onChange={handlePhoto}
/>
</div>
}
</div>
<div className="form-group">
{users.map(({ name }, i) => <label key={i} className="form-checkbox form-inline">
<input
type="checkbox"
checked={debtors[name]}
onChange={e => setDebtors({ ...debtors, [name]: e.target.checked })}
/>
<i className="form-icon"/> {name}
</label>)}
</div>
<img ref={check} id="check" alt="file upload check" style={{ display: photo ? 'initial' : 'none' }}/>
<button type={'submit'} className="btn btn-primary">Save</button>
</form>
{/* language=CSS */}
<style jsx>{`
img#check {
width: 100%;
}
.fileUpload {
position: relative;
overflow: hidden;
}
.fileUpload input {
position: absolute;
top: 0;
right: 0;
margin: 0;
padding: 0;
font-size: 20px;
cursor: pointer;
opacity: 0;
filter: alpha(opacity=0);
}
`}</style>
</Layout>
}
Profile.getInitialProps = async ctx => ({
users: await callAPI(ctx, { url: `/api/users/` }),
})
export default withAuthSync(Profile)