Skip to content

Commit

Permalink
feat(user-multi-selection): add component
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobias Pickel committed Jan 9, 2019
1 parent 61c1742 commit 62942d4
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 71 deletions.
114 changes: 43 additions & 71 deletions src/components/transaction/split-invoice/split-invoice.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {
AlertText,
Block,
CancelButton,
Card,
Input,
PrimaryButton,
ResponsiveGrid,
Expand All @@ -16,20 +13,22 @@ import {
CreateTransactionParams,
Transaction,
User,
UsersState,
startCreatingTransaction,
} from '../../../store/reducers';
import { ConnectedIdleTimer } from '../../common/idle-timer';
import { Currency, CurrencyInput } from '../../currency';
import { ConnectedUserSelectionList } from '../../user';
import { ConnectedTransactionListItem } from '../transaction-list-item';
import { ConnectedTransactionValidator } from '../validator';
import { ConnectedUserMultiSelection } from '../../user/user-multi-selection';
import { isTransactionValid } from '../validator';

interface State {
recipient: User | undefined;
participants: User[];
comment: string;
amount: number;
responseState: { [userId: number]: Transaction | 'error' };
validation: { [userId: number]: string };
}

interface Props {}
Expand All @@ -40,6 +39,7 @@ const initialState: State = {
comment: '',
amount: 0,
responseState: {},
validation: {},
};

const GridContainer = styled('div')({
Expand Down Expand Up @@ -96,16 +96,18 @@ export class SplitInvoiceForm extends React.Component<Props, State> {

public setAmount = (amount: number) => {
this.setState({ amount });
this.updateValidation();
};

public setComment = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ comment: e.target.value });
};

public addParticipant = (user: User) => {
this.setState(state => ({
participants: [...(state.participants || []), user],
}));
public addParticipant = (user: UsersState) => {
this.setState({
participants: Object.values(user),
});
this.updateValidation();
};

public removeParticipant = (userToRemove: User) => {
Expand Down Expand Up @@ -138,7 +140,8 @@ export class SplitInvoiceForm extends React.Component<Props, State> {
return (
this.state.recipient &&
this.state.participants.length &&
this.state.amount > 0
this.state.amount > 0 &&
this.formIsValid()
);
};

Expand All @@ -156,6 +159,33 @@ export class SplitInvoiceForm extends React.Component<Props, State> {
return transactionState === 'error';
};

public updateValidation = () => {
const value = this.getSplitAmount();
const boundary = store.getState().settings.payment.boundary;
const initialValue: { [key: number]: string } = {};
const validation = Object.values(this.state.participants).reduce(
(acc, participant) => {
return {
...acc,
[participant.id]: isTransactionValid({
value,
isDeposit: true,
boundary,
balance: participant.balance,
})
? ''
: `can't afford it`,
};
},
initialValue
);
this.setState({ validation });
};

public formIsValid = () => {
return Object.values(this.state.validation).every(item => item === '');
};

public render(): JSX.Element {
return (
<GridContainer>
Expand Down Expand Up @@ -226,70 +256,12 @@ export class SplitInvoiceForm extends React.Component<Props, State> {
)}
/>
<Block margin="1rem 0">
<ConnectedUserSelectionList
disabled={!(this.state.amount > 0 && this.state.recipient)}
userId={this.getRecipientUserId(this.state.recipient)}
getString={() => ''}
placeholder="add participant"
autoFocus
<ConnectedUserMultiSelection
validation={this.state.validation}
onSelect={this.addParticipant}
placeholder="add participant"
/>
</Block>

{this.state.participants.map(participant => (
<Block margin="0.5rem 0" key={participant.name}>
{this.getTransactionId(participant) ? (
<>
{participant.name}{' '}
<FormattedMessage id="PAYED" defaultMessage="payed" />
<ConnectedTransactionListItem
id={this.getTransactionId(participant)}
/>
</>
) : (
<Card
flex
justifyContent="space-between"
alignContent="center"
alignItems="center"
>
{this.hasErrorState(participant) ? (
<PrimaryButton
onClick={() => this.createTransaction(participant)}
>
<FormattedMessage id="RETRY" defaultMessage="retry" />
</PrimaryButton>
) : (
<CancelButton
onClick={() => this.removeParticipant(participant)}
/>
)}
<ConnectedTransactionValidator
userId={participant.id}
isDeposit
value={this.getSplitAmount()}
render={isValid => (
<>
{!isValid && this.state.amount > 0 && (
<FormattedMessage
id="SPLIT_INVOICE_USER_INVALID"
defaultMessage="You can't afford it"
/>
)}
</>
)}
/>{' '}
{participant.name}{' '}
<AlertText value={participant.balance - this.getSplitAmount()}>
<Currency
value={participant.balance - this.getSplitAmount()}
/>
</AlertText>
</Card>
)}
</Block>
))}
<div />
</GridContainer>
);
}
Expand Down
48 changes: 48 additions & 0 deletions src/components/user/user-multi-selection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { MultiSelectionBox } from 'bricks-of-sand';
import * as React from 'react';
import { connect } from 'react-redux';
import { AppState } from '../../store';
import { User, UsersState } from '../../store/reducers';

interface OwnProps {
placeholder: string;
validation: { [userId: number]: string };
onSelect(selection: UsersState): void;
}

interface StateProps {
users: UsersState;
}

interface ActionProps {}

export type UserMultiSelectionProps = ActionProps & StateProps & OwnProps;

export function UserMultiSelection({
placeholder,
onSelect,
users,
validation,
}: UserMultiSelectionProps): JSX.Element | null {
return (
<MultiSelectionBox
errorMessageMap={validation}
getItemIndex={user => (user ? user.id : 0)}
itemToString={user => user.name}
items={users}
onSelect={onSelect}
placeholder={placeholder}
/>
);
}

const mapStateToProps = (state: AppState): StateProps => ({
users: state.user,
});

const mapDispatchToProps = {};

export const ConnectedUserMultiSelection = connect(
mapStateToProps,
mapDispatchToProps
)(UserMultiSelection);

0 comments on commit 62942d4

Please sign in to comment.