Skip to content
This repository was archived by the owner on Jul 2, 2023. It is now read-only.

Commit ea7c51a

Browse files
Merge pull request #93 from AndresMorelos/subitems
Subitems
2 parents 6192897 + 313cafd commit ea7c51a

File tree

13 files changed

+609
-394
lines changed

13 files changed

+609
-394
lines changed

app/actions/form.jsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,35 @@ import * as ACTION_TYPES from '../constants/actions.jsx';
44
// Recipient
55
export const updateRecipient = createAction(
66
ACTION_TYPES.FORM_RECIPIENT_UPDATE,
7-
data => data
7+
(data) => data
88
);
99

1010
// ItemsRow
1111
export const addItem = createAction(ACTION_TYPES.FORM_ITEM_ADD);
1212

1313
export const removeItem = createAction(
1414
ACTION_TYPES.FORM_ITEM_REMOVE,
15-
itemID => itemID
15+
(itemID) => itemID
1616
);
1717

1818
export const updateItem = createAction(
1919
ACTION_TYPES.FORM_ITEM_UPDATE,
20-
itemData => itemData
20+
(itemData) => itemData
21+
);
22+
23+
export const addSubItem = createAction(
24+
ACTION_TYPES.FORM_ITEM_ADD_SUBITEM,
25+
(itemID) => itemID
26+
);
27+
28+
export const removeSubItem = createAction(
29+
ACTION_TYPES.FORM_ITEM_REMOVE_SUBITEM,
30+
(parentItemId, itemID) => ({ id: itemID, parentItemId })
31+
);
32+
33+
export const updateSubItem = createAction(
34+
ACTION_TYPES.FORM_ITEM_UPDATE_SUBITEM,
35+
(parentItemId, itemData) => ({ subItem: itemData, parentItemId })
2136
);
2237

2338
export const moveRow = createAction(
@@ -30,12 +45,12 @@ export const addPaymentItem = createAction(ACTION_TYPES.FORM_PAYMENT_ITEM_ADD);
3045

3146
export const removePaymentItem = createAction(
3247
ACTION_TYPES.FORM_PAYMENT_ITEM_REMOVE,
33-
itemID => itemID
48+
(itemID) => itemID
3449
);
3550

3651
export const updatePaymentItem = createAction(
3752
ACTION_TYPES.FORM_PAYMENT_ITEM_UPDATE,
38-
itemData => itemData
53+
(itemData) => itemData
3954
);
4055

4156
export const movePaymentRow = createAction(
@@ -72,5 +87,5 @@ export const updateFieldData = createAction(
7287

7388
export const toggleField = createAction(
7489
ACTION_TYPES.FORM_FIELD_TOGGLE,
75-
field => field
90+
(field) => field
7691
);

app/components/form/ItemRow.jsx

Lines changed: 117 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,17 @@ import { compose } from 'recompose';
77
import styled from 'styled-components';
88
import _withDraggable from './hoc/_withDraggable';
99

10+
// Custom Components
11+
import SubItemsRow from './SubItemsRow';
12+
import { Section } from '../shared/Section';
13+
1014
// Styles
1115

16+
const ItemsListDiv = styled.div`
17+
position: relative;
18+
margin-bottom: 10px;
19+
`;
20+
1221
const ItemDiv = styled.div`
1322
position: relative;
1423
display: flex;
@@ -53,6 +62,13 @@ const ItemRemoveBtn = styled.a`
5362
}
5463
`;
5564

65+
const ItemAddSubiTemBtn = styled.a`
66+
> i {
67+
color: #00a8ff;
68+
margin-right: 10px;
69+
}
70+
`;
71+
5672
// Component
5773
export class ItemRow extends Component {
5874
constructor(props) {
@@ -63,18 +79,22 @@ export class ItemRow extends Component {
6379
this.updateSubtotal = this.updateSubtotal.bind(this);
6480
this.uploadRowState = this.uploadRowState.bind(this);
6581
this.removeRow = this.removeRow.bind(this);
82+
this.addSubItem = this.addSubItem.bind(this);
83+
this.removeSubItem = this.removeSubItem.bind(this);
84+
this.updateSubItem = this.updateSubItem.bind(this);
6685
}
6786

6887
UNSAFE_componentWillMount() {
69-
const { id, description, quantity, price, subtotal } = this.props.item;
70-
const index = this.props.index;
88+
const { subItems, item, index } = this.props;
89+
const { id, description, quantity, price, subtotal } = item;
7190
this.setState({
7291
id,
7392
index,
7493
description: description || '',
7594
price: price || '',
7695
quantity: quantity || '',
7796
subtotal: subtotal || '',
97+
subitems: subItems || [],
7898
});
7999
}
80100

@@ -118,81 +138,123 @@ export class ItemRow extends Component {
118138
}
119139

120140
uploadRowState() {
121-
const { updateRow } = this.props;
122-
updateRow(this.state);
141+
const { updateRow, subItems } = this.props;
142+
updateRow({
143+
...this.state,
144+
subitems: [...subItems, ...this.state.subitems],
145+
});
123146
}
124147

125148
removeRow() {
126149
this.props.removeRow(this.state.id);
127150
}
128151

152+
addSubItem() {
153+
this.props.addSubItem(this.state.id);
154+
}
155+
156+
removeSubItem(itemId) {
157+
this.props.removeSubItem(this.state.id, itemId);
158+
}
159+
160+
updateSubItem(item) {
161+
this.props.updateSubItem(this.state.id, item);
162+
}
163+
129164
render() {
130-
const { t, actions, hasHandler } = this.props;
165+
const { t, actions, hasHandler, subitemActions, subItems } = this.props;
166+
167+
const SubItemsRowsComponent = subItems.map((subItem, index) => (
168+
<SubItemsRow
169+
key={subItem.id}
170+
item={subItem}
171+
index={index}
172+
t={t}
173+
removeSubItem={this.removeSubItem}
174+
updateSubItem={this.updateSubItem}
175+
/>
176+
));
177+
131178
return (
132-
<ItemDiv>
133-
{hasHandler && (
134-
<div className="dragHandler">
135-
<i className="ion-grid" />
179+
<>
180+
<ItemDiv>
181+
{hasHandler && (
182+
<div className="dragHandler">
183+
<i className="ion-grid" />
184+
</div>
185+
)}
186+
<div className="flex3">
187+
<ItemDivInput
188+
name="description"
189+
type="text"
190+
value={this.state.description}
191+
onChange={this.handleTextInputChange}
192+
onKeyDown={this.handleKeyDown}
193+
placeholder={t('form:fields:items:description')}
194+
/>
136195
</div>
137-
)}
138-
<div className="flex3">
139-
<ItemDivInput
140-
name="description"
141-
type="text"
142-
value={this.state.description}
143-
onChange={this.handleTextInputChange}
144-
onKeyDown={this.handleKeyDown}
145-
placeholder={t('form:fields:items:description')}
146-
/>
147-
</div>
148-
149-
<div className="flex1">
150-
<ItemDivInput
151-
name="price"
152-
type="number"
153-
step="0.01"
154-
value={this.state.price}
155-
onChange={this.handleNumberInputChange}
156-
onKeyDown={this.handleKeyDown}
157-
placeholder={t('form:fields:items:price')}
158-
/>
159-
</div>
160-
161-
<div className="flex1">
162-
<ItemDivInput
163-
name="quantity"
164-
type="number"
165-
step="0.01"
166-
value={this.state.quantity}
167-
onChange={this.handleNumberInputChange}
168-
onKeyDown={this.handleKeyDown}
169-
placeholder={t('form:fields:items:quantity')}
170-
/>
171-
</div>
172-
173-
{(actions || hasHandler) && (
174-
<ItemActions>
175-
{actions && (
176-
<ItemRemoveBtn href="#" onClick={this.removeRow}>
177-
<i className="ion-close-circled" />
178-
</ItemRemoveBtn>
179-
)}
180-
</ItemActions>
181-
)}
182-
</ItemDiv>
196+
197+
<div className="flex1">
198+
<ItemDivInput
199+
name="price"
200+
type="number"
201+
step="0.01"
202+
value={this.state.price}
203+
onChange={this.handleNumberInputChange}
204+
onKeyDown={this.handleKeyDown}
205+
placeholder={t('form:fields:items:price')}
206+
/>
207+
</div>
208+
209+
<div className="flex1">
210+
<ItemDivInput
211+
name="quantity"
212+
type="number"
213+
step="0.01"
214+
value={this.state.quantity}
215+
onChange={this.handleNumberInputChange}
216+
onKeyDown={this.handleKeyDown}
217+
placeholder={t('form:fields:items:quantity')}
218+
/>
219+
</div>
220+
221+
{(actions || hasHandler || subitemActions) && (
222+
<ItemActions>
223+
{subitemActions && (
224+
<ItemAddSubiTemBtn href="#" onClick={this.addSubItem}>
225+
<i className="ion-plus-circled" />
226+
</ItemAddSubiTemBtn>
227+
)}
228+
{actions && (
229+
<ItemRemoveBtn href="#" onClick={this.removeRow}>
230+
<i className="ion-close-circled" />
231+
</ItemRemoveBtn>
232+
)}
233+
</ItemActions>
234+
)}
235+
</ItemDiv>
236+
<Section>
237+
<ItemsListDiv>{SubItemsRowsComponent}</ItemsListDiv>
238+
</Section>
239+
</>
183240
);
184241
}
185242
}
186243

187244
ItemRow.propTypes = {
188245
actions: PropTypes.bool.isRequired,
246+
subitemActions: PropTypes.bool.isRequired,
189247
addItem: PropTypes.func.isRequired,
190248
t: PropTypes.func.isRequired,
191249
hasHandler: PropTypes.bool.isRequired,
192250
item: PropTypes.object.isRequired,
251+
subItems: PropTypes.array.isRequired,
193252
index: PropTypes.number.isRequired,
194253
removeRow: PropTypes.func.isRequired,
195254
updateRow: PropTypes.func.isRequired,
255+
addSubItem: PropTypes.func.isRequired,
256+
removeSubItem: PropTypes.func.isRequired,
257+
updateSubItem: PropTypes.func.isRequired,
196258
};
197259

198260
export default compose(_withDraggable)(ItemRow);

app/components/form/ItemsList.jsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,34 @@ export class ItemsList extends PureComponent {
6969

7070
render() {
7171
// Bound Actions
72-
const { addItem, removeItem, updateItem } = this.props.boundActionCreators;
72+
const {
73+
addItem,
74+
removeItem,
75+
updateItem,
76+
addSubItem,
77+
removeSubItem,
78+
updateSubItem,
79+
} = this.props.boundActionCreators;
80+
7381
// Item Rows
7482
const { t, rows } = this.props;
75-
83+
7684
const rowsComponent = rows.map((item, index) => (
7785
<ItemRow
7886
key={item.id}
7987
item={item}
8088
index={index}
8189
t={t}
90+
subItems={item.subitems || []}
8291
hasHandler={rows.length > 1}
8392
actions={index !== 0}
93+
subitemActions
8494
updateRow={updateItem}
8595
removeRow={removeItem}
8696
addItem={addItem}
97+
addSubItem={addSubItem}
98+
removeSubItem={removeSubItem}
99+
updateSubItem={updateSubItem}
87100
/>
88101
));
89102

@@ -94,11 +107,7 @@ export class ItemsList extends PureComponent {
94107
<ItemsListHeader>
95108
<label className="itemLabel">{t('form:fields:items:name')} *</label>
96109
</ItemsListHeader>
97-
<ItemsListDiv>
98-
<TransitionList componentHeight={50}>
99-
{rowsComponent}
100-
</TransitionList>
101-
</ItemsListDiv>
110+
<ItemsListDiv>{rowsComponent}</ItemsListDiv>
102111
<div className="itemsListActions">
103112
<ItemsListActionsBtn primary onClick={addItem}>
104113
{t('form:fields:items:add')}
@@ -115,12 +124,12 @@ ItemsList.propTypes = {
115124
rows: PropTypes.arrayOf(PropTypes.object).isRequired,
116125
};
117126

118-
const mapStateToProps = state => ({
127+
const mapStateToProps = (state) => ({
119128
formState: state.form, // Make drag & drop works
120129
rows: getRows(state),
121130
});
122131

123-
const mapDispatchToProps = dispatch => ({
132+
const mapDispatchToProps = (dispatch) => ({
124133
boundActionCreators: bindActionCreators(Actions, dispatch),
125134
});
126135

0 commit comments

Comments
 (0)