Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payment Viewer Frontend page #73

Merged
merged 5 commits into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@ matrix:
- pipenv run python src/manage.py test
- language: node_js
node_js: 8
install:
- npm install
before_install:
- cd frontend
script:
- npm test
4 changes: 3 additions & 1 deletion backend/src/dutch_broomstick/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ class UserDetailView(generics.RetrieveUpdateAPIView):

class RoomListCreateView(generics.ListCreateAPIView):
permission_classes = (CheckUsername,)
queryset = Room.objects.all()
serializer_class = RoomSerializer

def get_queryset(self):
return Room.objects.filter(owner=self.request.user)

def perform_create(self, serializer):
serializer.save(owner=self.request.user)

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const App = () => {
<Route path="/user/setting/" component={UserInfoPage} exact />
<Route path="/user/create_room/" component={RoomCreatePage} exact />
<Route path="/user/:room_id/entrance/" component={EntrancePage} />
<Route path="/room/:room_id/payment/" component={PaymentPage} />
<Route path="/room/:room_id/payment/" component={PaymentPage} exact />
<Route path="/room/:room_id/payment/:payment_id/" component={PaymentPage} exact />
<Route path="/room/:room_id/setting/" component={RoomSettingPage} />
<Route path="/room/:room_id/member/:member/" component={IndividualPage} exact />
<Route path="/room/:room_id/member/:member/:to" component={AccountPage} />
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/components/atoms/Block/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import styled from 'styled-components'
import PropTypes from 'prop-types'

const alignItems = ({direction}) => (
direction === 'column' ?
'center' : 'baseline'
)

const Block = styled.div`
background-color: ${props => props.transparent ? "transparent" : "white"};
border: ${props => props.transparent ? "none" : "thin solid #bfbfbf"};
display: flex;
flex-direction: ${props => props.direction || "column"};
align-items: center;
flex-direction: ${({direction}) => direction};
justify-content: space-between;
align-items: ${alignItems};
margin: 0.5em auto 0;
padding: 1em 20px;
max-width: 310px;
Expand All @@ -17,4 +24,8 @@ const Block = styled.div`
}
`

Block.defaultProps = {
direction: 'column',
}

export default Block
16 changes: 15 additions & 1 deletion frontend/src/components/atoms/Button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,25 @@ const Button = styled.button`
font-size: 1em;
margin: ${({ horizontal }) => horizontal ? "0 0.5em" : "0.5em auto 0"};
padding: 4px 15px;
width: 100%;
width: ${({ width }) => width};
max-width: 224px;
z-index: 1;

:disabled {
background-color: lightgrey;
color: grey;
cursor: default;
}
`

Button.defaultProps = {
width: '100%',
}

Button.propTypes = {
width: PropTypes.string.isRequired,
}

const LightButton = styled(Button)`
background: white;
color: black;
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/atoms/Graph/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const Wrapper = styled.div`
padding-top: 100%;
width: 100%;
position: relative;
z-index: -1;
`

const Content = styled.div`
Expand Down
24 changes: 16 additions & 8 deletions frontend/src/components/organisms/PaymentForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,35 @@ const TinyInput = styled(SimpleInput)`
margin: 0;
`

const Credit = ({ member: {membername}, index, ...props }) => (
const natural = value => value && Math.max(Math.round(value), 0)
const lessThan = x => (value => value && Math.min(natural(value), x))

const Credit = ({ total, member: {membername}, index, ...props }) => (
<TwoLineBlock
upper={<Label>{membername}</Label>}
lower={
<Field
name={`credits[${index}].amount`}
type="number"
parse={value => value && Number(value)}
normalize={lessThan(total)}
component={TinyInput}
/>
}
/>
)

const PaymentForm = ({ handleSubmit, room, members, payment, amountLeft, total, nBbang, ...props }) => {
const PaymentForm = ({ handleSubmit, room, members, payment, amountLeft, total, ...rest }) => {
const {
disabled,
set1OverN, setRandom, // setter callback
} = rest
return (
<Form onSubmit={handleSubmit}>
<Block>
<FieldWithLabel label="결제 내용" name="forWhat" type="text" component={SimpleInput} required />
<FieldWithLabel label="결제자" name="fromWho" component={Select} required>
<option selected value="">-- 멤버 목록 --</option>
<option value="">-- 멤버 목록 --</option>
{members.map(
({ membername }) => (
<option key={membername} value={membername}>
Expand All @@ -51,22 +59,22 @@ const PaymentForm = ({ handleSubmit, room, members, payment, amountLeft, total,
label="결제 금액"
name="total"
type="number"
parse={value => value && Number(value)}
normalize={natural}
required
component={SimpleInput}
/>
</Block>
<Block direction="row">
<Button type="button" onClick={() => nBbang(total, members)} light horizontal>N빵</Button>
<Button type="button" light horizontal>랜덤</Button>
<Button type="button" onClick={() => set1OverN(total, members)} light horizontal>N빵</Button>
<Button type="button" onClick={() => setRandom(total, members)} light horizontal>랜덤</Button>
<Button type="button" light horizontal>각자</Button>
</Block>
<Block>
남은 돈 ({amountLeft})
<hr />
{members.map(
(member, index) => (
<Credit key={index} index={index} member={member} />
<Credit key={index} index={index} member={member} total={total} />
)
)}
</Block>
Expand All @@ -78,7 +86,7 @@ const PaymentForm = ({ handleSubmit, room, members, payment, amountLeft, total,
>
취소
</LinkButton>
<Button type="submit" horizontal>확인</Button>
<Button type="submit" horizontal disabled={disabled}>확인</Button>
</Block>
</Form>
)
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/organisms/PaymentList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ const PaymentList = ({ paymentlist, roomurl }) => (
<List>
{
paymentlist && paymentlist.map(
({ forWhat, fromWho, total }, idx) => (
<ListItem key={idx} title={`${forWhat}${fromWho}`} description={total} linkTo={`/room/${roomurl}/payment_list/${forWhat}`} />
({ id, forWhat, fromWho, total }, idx) => (
<ListItem key={idx}
title={`${forWhat} - ${fromWho}`}
description={total}
linkTo={`/room/${roomurl}/payment/${id}/`}
/>
)
)
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/pages/IndividualPage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const IndividualPage = ({ sendlist, getlist, roomurl, nickname }) => (
<List>
{
getlist && getlist.map(
({ to, label }, idx) => (
<ListItem key={idx} title={to} description={label} />
({ from, label }, idx) => (
<ListItem key={idx} title={from} description={label} />
)
)
}
Expand Down
27 changes: 23 additions & 4 deletions frontend/src/components/pages/RoomPage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,33 @@ const RoomPage = props => {
<Header />
<Block transparent>
<CopyToClipboard text={window.location.href} onCopy={() => {}}>
<Button>링크 복사</Button>
<Button width="auto" horizontal
style={{
'align-self': 'flex-end',
margin: '0 0 -2em'
}}
>
링크 복사
</Button>
</CopyToClipboard>
<Graph graph={graph} events={events} />
<SettingButton to={`/room/${room.url}/setting/`} />
</Block>
<Button onClick={onToggle}>
{showPayment ? "멤버 목록 보기" : "결제 목록 보기"}
</Button>
<Block direction="row" transparent
style={{margin: '0 auto', padding: '0'}}
>
<Button onClick={onToggle} width="auto" horizontal>
{showPayment ? "멤버 목록" : "결제 목록"}
</Button>
<h2 style={{
margin: '0 0.5em',
padding: '0 0.25em',
'border-bottom': 'solid',
color: 'dimgrey',
}}>
{room.roomname}
</h2>
</Block>
{showPayment ?
(<PaymentList />) :
(members && <MemberList />)
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/pages/UserPage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'

import { Block, Button, Header, List, ListItem } from 'components'
import { Block, Button, Header, List, LinkButton, ListItem } from 'components'

/**
* Presentational Components의 경우 redux 로직을 배제한다 (cf. pages/MainPage/index.js)
Expand All @@ -13,9 +13,9 @@ const UserPage = ({username, roomList, onClickUserInfo, onClickSignOut}) => (
<Header />
<Block transparent>
<h1>{username}</h1>
<Link to="/user/setting/">
<button onClick={onClickUserInfo}>유저 정보</button>
</Link>
<LinkButton to="/user/setting/" onClick={onClickUserInfo} width="auto">
유저 정보
</LinkButton>
</Block>
<Block>
새로운 방을 원한다면?
Expand Down
36 changes: 26 additions & 10 deletions frontend/src/containers/PaymentForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,41 @@ const mapStateToProps = state => ({
})

const mapDispatchToProps = dispatch => ({
nBbang(total, members) {
const amount = total / members.length
set1OverN(total, members) {
const amount = Math.floor(total / members.length)
members.forEach(
(m, index) =>
dispatch(change(FORM_NAME, `credits[${index}].amount`, amount))
)
}
},
setRandom(total, members) {
const rand = members.map(() => Math.random())
const sum = rand.reduce((a, b) => (a + b), 0)

rand.map(x => Math.floor(total * x / sum)).forEach(
(r, index) =>
dispatch(change(FORM_NAME, `credits[${index}].amount`, r))
)
},
})

const PaymentFormContainer = ({...props}) => {
const initialValues = {
credits: props.members.map(
({ membername }) => ({ toWho: membername, amount: 0.0 })
),
room: props.room,
}
const PaymentFormContainer = ({payment, ...props}) => {
const initialValues = (payment ?
{ // if payment exists
...payment,
room: props.room,
} :
{ // if payment doesn't exist
credits: props.members.map(
({ membername }) => ({ toWho: membername, amount: 0.0 })
),
room: props.room,
}
)

return (
<PaymentReduxForm
disabled={!!payment}
initialValues={initialValues}
{...props}
/>
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/containers/PaymentPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { connect } from 'react-redux'

import { PaymentPage } from 'components'

const PaymentPageContainer = props => {
return <PaymentPage {...props} />
const PaymentPageContainer = ({ match, payments, ...props }) => {
const paymentId = parseInt(match.params.payment_id)
const payment = payments.find(p => p.id === paymentId)
return <PaymentPage payment={payment} {...props} />
}

const mapStateToProps = state => ({
room: state.room.room,
members: state.member.members,
payment: state.payment.payment,
payments: state.payment.payments,
})

export default connect(mapStateToProps)(PaymentPageContainer)
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/containers/RoomCreateForm.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import { connect } from 'react-redux'
import { reduxForm, formValueSelector, arrayPush, change } from 'redux-form'
import { toastr } from 'react-redux-toastr'
import { reduxForm, formValueSelector, arrayPush, change, SubmissionError } from 'redux-form'
import { RoomCreateForm } from 'components'

import { roomCreateRequest } from 'store/actions'
Expand Down Expand Up @@ -33,6 +34,14 @@ export default connect(mapStateToProps, mapDispatchToProps)(
onSubmit(values, dispatch, props) {
const { roomname, members } = values
const { username, token } = props // username means ownername

if (!(members && members.length)) {
toastr.light(
"방 생성 오류", "화면 하단에서 다른 멤버를 최소 1명 추가해주세요.",
{ icon: 'error', status: 'error' }
)
return
}
dispatch(roomCreateRequest(roomname, members, username, token))
}
})(RoomCreateFormContainer)
Expand Down
10 changes: 3 additions & 7 deletions frontend/src/containers/RoomPage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { Redirect } from 'react-router-dom'
import { push } from 'connected-react-router'
import { connect } from 'react-redux'
import { RoomPage } from 'components'

Expand All @@ -14,11 +14,7 @@ class RoomPageContainer extends React.Component {
}

render() {
const { room, member } = this.props

if (member) {
return <Redirect to={`/room/${room.url}/member/${member.id}/`} />
}
const { room, } = this.props

if (room) {
document.title = `${this.props.room.roomname} - Dutch Broomstick`
Expand All @@ -31,7 +27,6 @@ class RoomPageContainer extends React.Component {

const mapStateToProps = state => ({
room: state.room.room,
member: state.room.member,
members: state.member.members,
payments: state.payment.payments,
showPayment: state.room.showPayment,
Expand All @@ -42,6 +37,7 @@ const mapDispatchToProps = dispatch => ({
onLeave: () => dispatch(roomLeave()),
onClickMember: (member, sendlist, getlist) => {
dispatch(roomSetMember(member, sendlist, getlist))
dispatch(push(`member/${member.id}/`))
},
onToggle: () => dispatch(roomToggleContents()),
})
Expand Down
Loading