disco_app 0.8.9 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/disco_app/icons.svg +0 -0
  3. data/app/assets/javascripts/disco_app/components/{filterable_shop_list.js.jsx → custom/filterable_shop_list.js.jsx} +1 -1
  4. data/app/assets/javascripts/disco_app/components/custom/inline-radio-options.es6.jsx +59 -0
  5. data/app/assets/javascripts/disco_app/components/custom/rules-editor.es6.jsx +360 -0
  6. data/app/assets/javascripts/disco_app/components/{shop_list.js.jsx → custom/shop_list.js.jsx} +9 -11
  7. data/app/assets/javascripts/disco_app/components/custom/shop_row.js.jsx +43 -0
  8. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card-section.es6.jsx +30 -0
  9. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card.es6.jsx +9 -0
  10. data/app/assets/javascripts/disco_app/components/ui-kit/cards/cart-section-title.es6.jsx +7 -0
  11. data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_form.es6.jsx +72 -0
  12. data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_input.es6.jsx +20 -0
  13. data/app/assets/javascripts/disco_app/components/ui-kit/forms/button.es6.jsx +13 -0
  14. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-radio.es6.jsx +30 -0
  15. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-select.es6.jsx +39 -0
  16. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-text.es6.jsx +59 -0
  17. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-textarea.es6.jsx +48 -0
  18. data/app/assets/javascripts/disco_app/components/ui-kit/icons/icon-chevron.es6.jsx +33 -0
  19. data/app/assets/javascripts/disco_app/components/ui-kit/icons/next-icon.es6.jsx +18 -0
  20. data/app/assets/javascripts/disco_app/components/ui-kit/input_select.es6.jsx +21 -0
  21. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-annotated-section.es6.jsx +29 -0
  22. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-empty-state.es6.jsx +35 -0
  23. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-footer-help.es6.jsx +13 -0
  24. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-page-actions.es6.jsx +39 -0
  25. data/app/assets/javascripts/disco_app/components.js +1 -4
  26. data/app/assets/javascripts/disco_app/disco_app.js +3 -0
  27. data/app/assets/javascripts/disco_app/ui-kit.js +1 -0
  28. data/app/assets/stylesheets/disco_app/admin/_header.scss +66 -0
  29. data/app/assets/stylesheets/disco_app/admin/_layout.scss +40 -0
  30. data/app/assets/stylesheets/disco_app/admin/_nav.scss +172 -0
  31. data/app/assets/stylesheets/disco_app/admin.scss +11 -0
  32. data/app/assets/stylesheets/disco_app/disco_app.scss +12 -12
  33. data/app/assets/stylesheets/disco_app/{disco/mixins → mixins}/_flexbox.scss +6 -0
  34. data/app/assets/stylesheets/disco_app/ui-kit/_ui-empty-state.scss +94 -0
  35. data/app/assets/stylesheets/disco_app/ui-kit/_ui-footer-help.scss +25 -0
  36. data/app/assets/stylesheets/disco_app/ui-kit/_ui-icons.scss +28 -0
  37. data/app/assets/stylesheets/disco_app/ui-kit/_ui-kit.scss +5086 -0
  38. data/app/assets/stylesheets/disco_app/ui-kit/_ui-layout.scss +10 -0
  39. data/app/assets/stylesheets/disco_app/ui-kit/_ui-page-actions.scss +21 -0
  40. data/app/assets/stylesheets/disco_app/{disco/_tabs.scss → ui-kit/_ui-tabs.scss} +3 -1
  41. data/app/assets/stylesheets/disco_app/ui-kit/_ui-type.scss +13 -0
  42. data/app/controllers/disco_app/admin/concerns/plans_controller.rb +8 -5
  43. data/app/controllers/disco_app/admin/concerns/subscriptions_controller.rb +32 -0
  44. data/app/controllers/disco_app/admin/subscriptions_controller.rb +3 -0
  45. data/app/helpers/disco_app/application_helper.rb +22 -0
  46. data/app/models/disco_app/concerns/plan.rb +2 -2
  47. data/app/models/disco_app/concerns/shop.rb +0 -4
  48. data/app/models/disco_app/concerns/subscription.rb +12 -0
  49. data/app/resources/disco_app/admin/resources/concerns/shop_resource.rb +40 -3
  50. data/app/views/disco_app/admin/plans/_form.html.erb +18 -21
  51. data/app/views/disco_app/admin/plans/_plan_code_fields.html.erb +15 -0
  52. data/app/views/disco_app/admin/plans/index.html.erb +2 -0
  53. data/app/views/disco_app/admin/shops/index.html.erb +2 -1
  54. data/app/views/disco_app/admin/subscriptions/edit.html.erb +33 -0
  55. data/app/views/disco_app/shared/_icons.html.erb +3 -0
  56. data/app/views/layouts/admin/_nav_items.erb +20 -0
  57. data/app/views/layouts/admin.html.erb +53 -9
  58. data/app/views/layouts/embedded_app.html.erb +2 -0
  59. data/app/views/layouts/embedded_app_modal.html.erb +11 -0
  60. data/config/routes.rb +4 -2
  61. data/lib/disco_app/engine.rb +2 -1
  62. data/lib/disco_app/version.rb +1 -1
  63. data/lib/generators/disco_app/disco_app_generator.rb +35 -2
  64. data/lib/generators/disco_app/templates/assets/javascripts/components.js +3 -0
  65. data/lib/generators/disco_app/templates/config/database.yml.tt +20 -0
  66. data/lib/tasks/database.rake +8 -0
  67. data/test/controllers/disco_app/charges_controller_test.rb +9 -2
  68. data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_request.json +1 -1
  69. data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_request.json +1 -1
  70. data/test/fixtures/disco_app/subscriptions.yml +1 -0
  71. data/test/models/disco_app/subscription_test.rb +19 -0
  72. data/test/services/disco_app/charges_service_test.rb +9 -2
  73. data/test/test_helper.rb +3 -0
  74. metadata +80 -21
  75. data/app/assets/javascripts/disco_app/components/shop_row.js.jsx +0 -27
  76. data/app/assets/stylesheets/disco_app/bootstrap/_custom.scss +0 -54
  77. data/app/assets/stylesheets/disco_app/bootstrap/_variables.scss +0 -872
  78. data/app/assets/stylesheets/disco_app/disco/_buttons.scss +0 -31
  79. data/app/assets/stylesheets/disco_app/disco/_cards.scss +0 -52
  80. data/app/assets/stylesheets/disco_app/disco/_forms.scss +0 -23
  81. data/app/assets/stylesheets/disco_app/disco/_grid.scss +0 -58
  82. data/app/assets/stylesheets/disco_app/disco/_sections.scss +0 -61
  83. data/app/assets/stylesheets/disco_app/disco/_tables.scss +0 -57
  84. data/app/assets/stylesheets/disco_app/disco/_type.scss +0 -39
  85. data/app/views/layouts/admin/_navbar.html.erb +0 -25
  86. data/lib/generators/disco_app/adminify/adminify_generator.rb +0 -35
  87. data/lib/generators/disco_app/reactify/reactify_generator.rb +0 -45
  88. /data/app/assets/javascripts/disco_app/components/{shop_filter_tab.js.jsx → custom/shop_filter_tab.js.jsx} +0 -0
  89. /data/app/assets/javascripts/disco_app/components/{shop_filter_tabs.js.jsx → custom/shop_filter_tabs.js.jsx} +0 -0
  90. /data/app/assets/javascripts/disco_app/components/{shopify_admin_link.js.jsx → custom/shopify_admin_link.js.jsx} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed3b05b40feb3571f36794c103cc6f661b3a2ac0b1319d8c8d6d3a113287f088
4
- data.tar.gz: c4ffb7c2ee220e9e7f2d8c5f8d66e5270e84223150862b32a696c1dcc414d679
3
+ metadata.gz: d734e517357675c9fec94abcfe6fd509c04f6f31c9bdcc8a762c3971f11589d3
4
+ data.tar.gz: 5d2dd2d731d021adc3fbdc5e37af68df9f2c013083ad10980dd5ed2707842e8b
5
5
  SHA512:
6
- metadata.gz: 0cad14dee7ceeee65abc7623b305ef328b9db9a572a1f4cd97840a3256ff00877d122090d472cd3340622fbfdcf7d3a7bac7dcf07b22e0ccb2db18f3c85f606b
7
- data.tar.gz: 66580b280acd0a16d6dc092b008a231172a20ef73f208737a36332c2f0c2d6d9e20585ee129654b6d984a11762475ec316a121d6e2e8c8d4749a53868c7fb550
6
+ metadata.gz: 57d5072e2eb9d1b6b12ab45aeeb77b253e3fdf8aad36d993ec9799f8fa61de4ae1c9212e5b25c49304f54d6892d594b7befad0f1851fc6578f52e09c82d47e55
7
+ data.tar.gz: 02b898b139e5d707dd30680559f3abf459c51ca6c652f626449af2721b449997ab9871b0f933ae38c61e925fdecd45cf7db467ee58f7f0418fb5075178c6e693
File without changes
@@ -52,7 +52,7 @@ var FilterableShopList = React.createClass({
52
52
  return (
53
53
  <div className="next-card">
54
54
  <ShopFilterTabs filterTabs={this.props.filterTabs} filter={this.state.filter} onFilterReplace={this.onFilterReplace} />
55
- <ShopList shopsUrl={this.props.shopsUrl} editShopUrl={this.props.editShopUrl} filter={this.state.filter} />
55
+ <ShopList shopsUrl={this.props.shopsUrl} editShopUrl={this.props.editShopUrl} editSubscriptionUrl={this.props.editSubscriptionUrl} filter={this.state.filter} />
56
56
  </div>
57
57
  );
58
58
  }
@@ -0,0 +1,59 @@
1
+ class InlineRadioOptions extends React.Component {
2
+
3
+ constructor(props) {
4
+ super(props);
5
+ this.state = {
6
+ value: props.value
7
+ }
8
+ }
9
+
10
+ onChange(value) {
11
+ this.setState({
12
+ value: value
13
+ })
14
+ }
15
+
16
+ render() {
17
+ const { name, label, options } = this.props;
18
+
19
+ const optionElements = options.map((option, optionIndex) => {
20
+ return (
21
+ <InputRadio
22
+ key={option.value}
23
+ name={name}
24
+ value={option.value}
25
+ label={option.label}
26
+ inline={true}
27
+ isLast={optionIndex === (options.length - 1)}
28
+ checked={option.value === this.state.value}
29
+ onChange={this.onChange.bind(this)}
30
+ />
31
+ );
32
+ });
33
+
34
+ return (
35
+ <CardSection wrappable={true}>
36
+ <div className="wrappable__item wrappable__item--no-flex">
37
+ <span>{label}</span>
38
+ </div>
39
+ <div className="wrappable__item">
40
+ {optionElements}
41
+ </div>
42
+ </CardSection>
43
+ );
44
+ }
45
+
46
+ }
47
+
48
+ InlineRadioOptions.propTypes = {
49
+ name: React.PropTypes.string,
50
+ options: React.PropTypes.arrayOf(
51
+ React.PropTypes.shape({
52
+ label: React.PropTypes.string.isRequired,
53
+ value: React.PropTypes.oneOfType([
54
+ React.PropTypes.string,
55
+ React.PropTypes.bool
56
+ ]).isRequired
57
+ })
58
+ ).isRequired
59
+ };
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Defines a generic RulesEditor class. This class can't be used directly, but
3
+ * should be inherited, with the inheriting class defining a list of column
4
+ * types and correponding relations and condition data types, like so:
5
+ *
6
+ * class MyRulesEditor extends RulesEditor {};
7
+ * MyRulesEditor.defaultProps = {
8
+ * columns: {
9
+ * title: {
10
+ * label: 'Product title',
11
+ * column: 'title',
12
+ * relations: {
13
+ * is_equal_to: {
14
+ * label: 'is equal to',
15
+ * relation: 'is_equal_to',
16
+ * type: 'text'
17
+ * }
18
+ * }
19
+ * }
20
+ * }
21
+ */
22
+ class RulesEditor extends React.Component {
23
+
24
+ /**
25
+ * Initialise the Rules Editor, and set the initial state.
26
+ * @param props
27
+ */
28
+ constructor(props) {
29
+ super(props);
30
+ this.state = {
31
+ rules: props.rules.map((rule, i) => {
32
+ return {
33
+ column: Object.keys(this.props.columns).filter((columnKey) => {
34
+ return this.props.columns[columnKey].column == rule.column;
35
+ })[0],
36
+ relation: rule.relation,
37
+ condition: rule.condition
38
+ }
39
+ })
40
+ }
41
+ }
42
+
43
+ componentDidMount() {
44
+ if(this.state.rules.length === 0 && this.props.blankOk === false) {
45
+ this.onAddRule();
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Add a new rule, with an immutable state change.
51
+ */
52
+ onAddRule() {
53
+ const column = Object.keys(this.props.columns)[0];
54
+ const relation = this.getNextRelation(column);
55
+ const condition = this.getNextCondition(column, relation);
56
+
57
+ this.setState({
58
+ rules: this.state.rules.concat([{
59
+ column,
60
+ relation,
61
+ condition
62
+ }])
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Remove a rule, with an immutable state change.
68
+ */
69
+ onRemoveRule(index) {
70
+ this.setState({
71
+ rules: [
72
+ ...this.state.rules.slice(0, index),
73
+ ...this.state.rules.slice(index + 1)
74
+ ]
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Handle a change in a rule's column attribute.
80
+ *
81
+ * @param index
82
+ * @param column
83
+ */
84
+ onRuleColumnChange(index, column) {
85
+ const relation = this.getNextRelation(column, this.state.rules[index]);
86
+ const condition = this.getNextCondition(column, relation, this.state.rules[index]);
87
+
88
+ this.updateRule(index, {
89
+ column,
90
+ relation,
91
+ condition
92
+ })
93
+ }
94
+
95
+ /**
96
+ * Handle a change in a rule's relation attribute.
97
+ *
98
+ * @param index
99
+ * @param relation
100
+ */
101
+ onRuleRelationChange(index, relation) {
102
+ const condition = this.getNextCondition(this.state.rules[index].column, relation, this.state.rules[index]);
103
+
104
+ this.updateRule(index, {
105
+ relation,
106
+ condition
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Handle a change in a rule's condition attribute.
112
+ *
113
+ * @param index
114
+ * @param condition
115
+ */
116
+ onRuleConditionChange(index, condition) {
117
+ this.updateRule(index, {
118
+ condition
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Given the column we're changing to and the current rule, return the next
124
+ * relation value.
125
+ *
126
+ * @param nextColumn
127
+ * @param currentRule
128
+ */
129
+ getNextRelation(nextColumn, currentRule) {
130
+ // If the new column provides for the same relation, keep it.
131
+ if(currentRule && (this.props.columns[nextColumn].relations[currentRule.relation] !== undefined)) {
132
+ return currentRule.relation;
133
+ }
134
+ // Otherwise, return the first relation for the new column.
135
+ return Object.keys(this.props.columns[nextColumn].relations)[0];
136
+ }
137
+
138
+ /**
139
+ * Given the column and relation we're changing to and the current condition,
140
+ * return the value that the condition should be changed to.
141
+ *
142
+ * @param nextColumn
143
+ * @param nextRelation
144
+ * @param currentRule
145
+ */
146
+ getNextCondition(nextColumn, nextRelation, currentRule) {
147
+ // If the new relation provides for the same condition type, keep it.
148
+ if(currentRule) {
149
+ const currentConditionType = this.props.columns[currentRule.column].relations[currentRule.relation].type;
150
+ const nextConditionType = this.props.columns[nextColumn].relations[nextRelation].type;
151
+
152
+ if(currentConditionType === nextConditionType) {
153
+ return currentRule.condition;
154
+ }
155
+ }
156
+
157
+ // Otherwise, reset the condition to an empty string.
158
+ return '';
159
+ }
160
+
161
+ /**
162
+ * Handle the updating of a rule in our array in an immutable manner.
163
+ *
164
+ * @param index
165
+ * @param updates
166
+ */
167
+ updateRule(index, updates) {
168
+ this.setState({
169
+ rules: [
170
+ ...this.state.rules.slice(0, index),
171
+ Object.assign({}, this.state.rules[index], updates),
172
+ ...this.state.rules.slice(index + 1)
173
+ ]
174
+ });
175
+ }
176
+
177
+ /**
178
+ * Render the Rules Editor.
179
+ */
180
+ render() {
181
+ const { name } = this.props;
182
+ const { rules } = this.state;
183
+
184
+ const ruleElements = rules.map((rule, i) => {
185
+ return <RulesEditorRule
186
+ key={i}
187
+ rule={rule}
188
+ columns={this.props.columns}
189
+ onRemove={this.onRemoveRule.bind(this, i)}
190
+ onColumnChange={this.onRuleColumnChange.bind(this, i)}
191
+ onRelationChange={this.onRuleRelationChange.bind(this, i)}
192
+ onConditionChange={this.onRuleConditionChange.bind(this, i)}
193
+ ruleCount={rules.length}
194
+ blankOk = {this.props.blankOk}
195
+ />
196
+ });
197
+
198
+ // Convert the current rules JSON into a format using the correct column
199
+ // format used by our more advanced key checker.
200
+ const rulesJSON = JSON.stringify(this.state.rules.map((rule, i) => {
201
+ return {
202
+ column: this.props.columns[rule.column].column,
203
+ relation: rule.relation,
204
+ condition: rule.condition
205
+ }
206
+ }));
207
+
208
+ return(
209
+ <CardSection>
210
+ <div className="next-grid next-grid--no-outside-padding">
211
+ <div style={{ width: '100%'}}>
212
+ {ruleElements}
213
+ </div>
214
+ </div>
215
+ <Button onClick={this.onAddRule.bind(this)}>
216
+ Add another condition
217
+ </Button>
218
+ <input type="hidden" name={name} value={rulesJSON} />
219
+ </CardSection>
220
+ );
221
+ }
222
+
223
+ static buildRelationsObj(relations) {
224
+ var relationsObj = {};
225
+
226
+ relations.forEach(function (relation) {
227
+ return relationsObj[relation.relation] = relation;
228
+ });
229
+
230
+ return relationsObj;
231
+ }
232
+
233
+ }
234
+
235
+
236
+ RulesEditor.EQUALS_STRING = {
237
+ label: 'is equal to',
238
+ relation: 'is_equal_to',
239
+ type: 'text'
240
+ };
241
+ RulesEditor.EQUALS_COUNTRY_CODE = {
242
+ label: 'is equal to',
243
+ relation: 'is_equal_to',
244
+ type: 'country_code'
245
+ };
246
+ RulesEditor.CONTAINS_STRING = {
247
+ label: 'contains',
248
+ relation: 'contains_string',
249
+ type: 'text'
250
+ };
251
+ RulesEditor.EQUALS_TAG = {
252
+ label: 'is equal to',
253
+ relation: 'find_in_set',
254
+ type: 'tag'
255
+ };
256
+ RulesEditor.GREATER_THAN = {
257
+ label: 'is greater than',
258
+ relation: 'is_greater_than',
259
+ type: 'numeric'
260
+ };
261
+ RulesEditor.LESS_THAN = {
262
+ label: 'is less than',
263
+ relation: 'is_less_than',
264
+ type: 'numeric'
265
+ };
266
+
267
+ const RulesEditorRule = ({ rule, columns, onRemove, onColumnChange, onRelationChange, onConditionChange, ruleCount, blankOk }) => {
268
+ const { column, relation, condition } = rule;
269
+
270
+ const currentColumn = columns[column];
271
+ const currentRelation = currentColumn.relations[relation];
272
+
273
+ let conditionEditor = null;
274
+ switch(currentRelation.type) {
275
+ case 'text':
276
+ case 'numeric':
277
+ case 'tag':
278
+ conditionEditor = <RulesEditorConditionInputText condition={condition} onChange={onConditionChange} />;
279
+ break;
280
+ case 'country_code':
281
+ conditionEditor = <RulesEditorConditionInputCountryCode condition={condition} onChange={onConditionChange} />;
282
+ break;
283
+ }
284
+
285
+ let deleteIconCell = null;
286
+ if(ruleCount > 1 || blankOk === true) {
287
+ deleteIconCell = (
288
+ <div className="sl">
289
+ <button type="button" className="btn btn--icon" onClick={onRemove}>
290
+ <i className="ico ico-14-svg ico-delete" />
291
+ </button>
292
+ </div>
293
+ );
294
+ }
295
+
296
+ return (
297
+ <div>
298
+ <div className="next-grid next-grid--no-padding next-grid--compact">
299
+ <div className="next-grid__cell">
300
+ <div className="next-grid next-grid--compact next-grid--no-outside-padding">
301
+ <div className="next-grid__cell">
302
+ <RulesEditorColumnSelect currentColumnName={column} columns={columns} onChange={onColumnChange} />
303
+ </div>
304
+ <div className="next-grid__cell">
305
+ <RulesEditorRelationSelect currentRelationName={relation} relations={currentColumn.relations} onChange={onRelationChange} />
306
+ </div>
307
+ <div className="next-grid__cell">
308
+ {conditionEditor}
309
+ </div>
310
+ </div>
311
+ </div>
312
+ {deleteIconCell}
313
+ </div>
314
+ <hr className="next-card__section__separator" />
315
+ </div>
316
+ );
317
+
318
+ };
319
+
320
+ const RulesEditorColumnSelect = ({ currentColumnName, columns, onChange }) => {
321
+ const options = Object.keys(columns).map((columnName) => {
322
+ return { label: columns[columnName].label, value: columnName }
323
+ });
324
+
325
+ return <InputSelect options={options} value={currentColumnName} onChange={onChange} label="Field" labelHidden={true} />;
326
+ };
327
+
328
+ const RulesEditorRelationSelect = ({ currentRelationName, relations, onChange }) => {
329
+ const options = Object.keys(relations).map((relationName) => {
330
+ return { label: relations[relationName].label, value: relationName }
331
+ });
332
+
333
+ return <InputSelect options={options} value={currentRelationName} onChange={onChange} label="Relation" labelHidden={true} />;
334
+ };
335
+
336
+ const RulesEditorConditionInputText = ({ condition, onChange }) => {
337
+
338
+ const handleChange = (e) => {
339
+ onChange && onChange(e);
340
+ };
341
+
342
+ return (
343
+ <InputText value={condition} onChange={handleChange} label="Value" labelHidden={true} />
344
+ );
345
+
346
+ };
347
+
348
+ const RulesEditorConditionInputCountryCode = ({ condition, onChange }) => {
349
+
350
+ const handleChange = (e) => {
351
+ onChange && onChange(e);
352
+ };
353
+
354
+ const countryOptions = [{"label":"Australia","value":"AU"},{"label":"Canada","value":"CA"},{"label":"United Kingdom","value":"GB"},{"label":"United States","value":"US"},{"label":"Afghanistan","value":"AF"},{"label":"Åland Islands","value":"AX"},{"label":"Albania","value":"AL"},{"label":"Algeria","value":"DZ"},{"label":"Andorra","value":"AD"},{"label":"Angola","value":"AO"},{"label":"Anguilla","value":"AI"},{"label":"Antigua & Barbuda","value":"AG"},{"label":"Argentina","value":"AR"},{"label":"Armenia","value":"AM"},{"label":"Aruba","value":"AW"},{"label":"Australia","value":"AU"},{"label":"Austria","value":"AT"},{"label":"Azerbaijan","value":"AZ"},{"label":"Bahamas","value":"BS"},{"label":"Bahrain","value":"BH"},{"label":"Bangladesh","value":"BD"},{"label":"Barbados","value":"BB"},{"label":"Belarus","value":"BY"},{"label":"Belgium","value":"BE"},{"label":"Belize","value":"BZ"},{"label":"Benin","value":"BJ"},{"label":"Bermuda","value":"BM"},{"label":"Bhutan","value":"BT"},{"label":"Bolivia","value":"BO"},{"label":"Bosnia & Herzegovina","value":"BA"},{"label":"Botswana","value":"BW"},{"label":"Bouvet Island","value":"BV"},{"label":"Brazil","value":"BR"},{"label":"British Indian Ocean Territory","value":"IO"},{"label":"British Virgin Islands","value":"VG"},{"label":"Brunei","value":"BN"},{"label":"Bulgaria","value":"BG"},{"label":"Burkina Faso","value":"BF"},{"label":"Burundi","value":"BI"},{"label":"Cambodia","value":"KH"},{"label":"Cameroon","value":"CM"},{"label":"Canada","value":"CA"},{"label":"Cape Verde","value":"CV"},{"label":"Cayman Islands","value":"KY"},{"label":"Central African Republic","value":"CF"},{"label":"Chad","value":"TD"},{"label":"Chile","value":"CL"},{"label":"China","value":"CN"},{"label":"Christmas Island","value":"CX"},{"label":"Cocos (Keeling) Islands","value":"CC"},{"label":"Colombia","value":"CO"},{"label":"Comoros","value":"KM"},{"label":"Congo - Brazzaville","value":"CG"},{"label":"Congo - Kinshasa","value":"CD"},{"label":"Cook Islands","value":"CK"},{"label":"Costa Rica","value":"CR"},{"label":"Croatia","value":"HR"},{"label":"Cuba","value":"CU"},{"label":"Curaçao","value":"CW"},{"label":"Cyprus","value":"CY"},{"label":"Czech Republic","value":"CZ"},{"label":"Côte d’Ivoire","value":"CI"},{"label":"Denmark","value":"DK"},{"label":"Djibouti","value":"DJ"},{"label":"Dominica","value":"DM"},{"label":"Dominican Republic","value":"DO"},{"label":"Ecuador","value":"EC"},{"label":"Egypt","value":"EG"},{"label":"El Salvador","value":"SV"},{"label":"Equatorial Guinea","value":"GQ"},{"label":"Eritrea","value":"ER"},{"label":"Estonia","value":"EE"},{"label":"Ethiopia","value":"ET"},{"label":"Falkland Islands","value":"FK"},{"label":"Faroe Islands","value":"FO"},{"label":"Fiji","value":"FJ"},{"label":"Finland","value":"FI"},{"label":"France","value":"FR"},{"label":"French Guiana","value":"GF"},{"label":"French Polynesia","value":"PF"},{"label":"French Southern Territories","value":"TF"},{"label":"Gabon","value":"GA"},{"label":"Gambia","value":"GM"},{"label":"Georgia","value":"GE"},{"label":"Germany","value":"DE"},{"label":"Ghana","value":"GH"},{"label":"Gibraltar","value":"GI"},{"label":"Greece","value":"GR"},{"label":"Greenland","value":"GL"},{"label":"Grenada","value":"GD"},{"label":"Guadeloupe","value":"GP"},{"label":"Guatemala","value":"GT"},{"label":"Guernsey","value":"GG"},{"label":"Guinea","value":"GN"},{"label":"Guinea-Bissau","value":"GW"},{"label":"Guyana","value":"GY"},{"label":"Haiti","value":"HT"},{"label":"Heard & McDonald Islands","value":"HM"},{"label":"Honduras","value":"HN"},{"label":"Hong Kong SAR China","value":"HK"},{"label":"Hungary","value":"HU"},{"label":"Iceland","value":"IS"},{"label":"India","value":"IN"},{"label":"Indonesia","value":"ID"},{"label":"Iran","value":"IR"},{"label":"Iraq","value":"IQ"},{"label":"Ireland","value":"IE"},{"label":"Isle of Man","value":"IM"},{"label":"Israel","value":"IL"},{"label":"Italy","value":"IT"},{"label":"Jamaica","value":"JM"},{"label":"Japan","value":"JP"},{"label":"Jersey","value":"JE"},{"label":"Jordan","value":"JO"},{"label":"Kazakhstan","value":"KZ"},{"label":"Kenya","value":"KE"},{"label":"Kiribati","value":"KI"},{"label":"Kosovo","value":"KV"},{"label":"Kuwait","value":"KW"},{"label":"Kyrgyzstan","value":"KG"},{"label":"Laos","value":"LA"},{"label":"Latvia","value":"LV"},{"label":"Lebanon","value":"LB"},{"label":"Lesotho","value":"LS"},{"label":"Liberia","value":"LR"},{"label":"Libya","value":"LY"},{"label":"Liechtenstein","value":"LI"},{"label":"Lithuania","value":"LT"},{"label":"Luxembourg","value":"LU"},{"label":"Macau SAR China","value":"MO"},{"label":"Macedonia","value":"MK"},{"label":"Madagascar","value":"MG"},{"label":"Malawi","value":"MW"},{"label":"Malaysia","value":"MY"},{"label":"Maldives","value":"MV"},{"label":"Mali","value":"ML"},{"label":"Malta","value":"MT"},{"label":"Martinique","value":"MQ"},{"label":"Mauritania","value":"MR"},{"label":"Mauritius","value":"MU"},{"label":"Mayotte","value":"YT"},{"label":"Mexico","value":"MX"},{"label":"Moldova","value":"MD"},{"label":"Monaco","value":"MC"},{"label":"Mongolia","value":"MN"},{"label":"Montenegro","value":"ME"},{"label":"Montserrat","value":"MS"},{"label":"Morocco","value":"MA"},{"label":"Mozambique","value":"MZ"},{"label":"Myanmar (Burma)","value":"MM"},{"label":"Namibia","value":"NA"},{"label":"Nauru","value":"NR"},{"label":"Nepal","value":"NP"},{"label":"Netherlands","value":"NL"},{"label":"Netherlands Antilles","value":"AN"},{"label":"New Caledonia","value":"NC"},{"label":"New Zealand","value":"NZ"},{"label":"Nicaragua","value":"NI"},{"label":"Niger","value":"NE"},{"label":"Nigeria","value":"NG"},{"label":"Niue","value":"NU"},{"label":"Norfolk Island","value":"NF"},{"label":"North Korea","value":"KP"},{"label":"Norway","value":"NO"},{"label":"Oman","value":"OM"},{"label":"Pakistan","value":"PK"},{"label":"Palestinian Territories","value":"PS"},{"label":"Panama","value":"PA"},{"label":"Papua New Guinea","value":"PG"},{"label":"Paraguay","value":"PY"},{"label":"Peru","value":"PE"},{"label":"Philippines","value":"PH"},{"label":"Pitcairn Islands","value":"PN"},{"label":"Poland","value":"PL"},{"label":"Portugal","value":"PT"},{"label":"Qatar","value":"QA"},{"label":"Réunion","value":"RE"},{"label":"Romania","value":"RO"},{"label":"Russia","value":"RU"},{"label":"Rwanda","value":"RW"},{"label":"Samoa","value":"WS"},{"label":"San Marino","value":"SM"},{"label":"São Tomé & Príncipe","value":"ST"},{"label":"Saudi Arabia","value":"SA"},{"label":"Senegal","value":"SN"},{"label":"Serbia","value":"RS"},{"label":"Seychelles","value":"SC"},{"label":"Sierra Leone","value":"SL"},{"label":"Singapore","value":"SG"},{"label":"Sint Maarten","value":"SX"},{"label":"Slovakia","value":"SK"},{"label":"Slovenia","value":"SI"},{"label":"Solomon Islands","value":"SB"},{"label":"Somalia","value":"SO"},{"label":"South Africa","value":"ZA"},{"label":"South Georgia & South Sandwich Islands","value":"GS"},{"label":"South Korea","value":"KR"},{"label":"Spain","value":"ES"},{"label":"Sri Lanka","value":"LK"},{"label":"St. Barthélemy","value":"BL"},{"label":"St. Helena","value":"SH"},{"label":"St. Kitts & Nevis","value":"KN"},{"label":"St. Lucia","value":"LC"},{"label":"St. Martin","value":"MF"},{"label":"St. Pierre & Miquelon","value":"PM"},{"label":"St. Vincent & Grenadines","value":"VC"},{"label":"Sudan","value":"SD"},{"label":"Suriname","value":"SR"},{"label":"Svalbard & Jan Mayen","value":"SJ"},{"label":"Swaziland","value":"SZ"},{"label":"Sweden","value":"SE"},{"label":"Switzerland","value":"CH"},{"label":"Syria","value":"SY"},{"label":"Taiwan","value":"TW"},{"label":"Tajikistan","value":"TJ"},{"label":"Tanzania","value":"TZ"},{"label":"Thailand","value":"TH"},{"label":"Timor-Leste","value":"TL"},{"label":"Togo","value":"TG"},{"label":"Tokelau","value":"TK"},{"label":"Tonga","value":"TO"},{"label":"Trinidad & Tobago","value":"TT"},{"label":"Tunisia","value":"TN"},{"label":"Turkey","value":"TR"},{"label":"Turkmenistan","value":"TM"},{"label":"Turks & Caicos Islands","value":"TC"},{"label":"Tuvalu","value":"TV"},{"label":"U.S. Outlying Islands","value":"UM"},{"label":"Uganda","value":"UG"},{"label":"Ukraine","value":"UA"},{"label":"United Arab Emirates","value":"AE"},{"label":"United Kingdom","value":"GB"},{"label":"United States","value":"US"},{"label":"Uruguay","value":"UY"},{"label":"Uzbekistan","value":"UZ"},{"label":"Vanuatu","value":"VU"},{"label":"Vatican City","value":"VA"},{"label":"Venezuela","value":"VE"},{"label":"Vietnam","value":"VN"},{"label":"Wallis & Futuna","value":"WF"},{"label":"Western Sahara","value":"EH"},{"label":"Yemen","value":"YE"},{"label":"Zambia","value":"ZM"},{"label":"Zimbabwe","value":"ZW"}];
355
+
356
+ return (
357
+ <InputSelect options={countryOptions} value={condition} onChange={handleChange} label="Value" labelHidden={true} />
358
+ );
359
+
360
+ };
@@ -24,7 +24,7 @@ var ShopList = React.createClass({
24
24
 
25
25
  componentDidUpdate: function (prevProps, prevState) {
26
26
  if(prevProps.filter != this.props.filter) {
27
- this.getShops();
27
+ this.onShopsChanged();
28
28
  }
29
29
  if(prevState.page != this.state.page) {
30
30
  this.getShops();
@@ -35,6 +35,7 @@ var ShopList = React.createClass({
35
35
  this.setState({page: this.state.page + increment});
36
36
  },
37
37
 
38
+ /* get shops with a different filter */
38
39
  onShopsChanged: function() {
39
40
  this.setState({page: 1});
40
41
  this.getShops();
@@ -66,7 +67,7 @@ var ShopList = React.createClass({
66
67
  var shopRows = this.state.shops.map(function(shop, index) {
67
68
  if ((index >= ((this.state.page - 1) * this.props.pageSize)) && (index < this.state.page * this.props.pageSize)) {
68
69
  return (
69
- <ShopRow shop={shop} editShopUrl={this.props.editShopUrl} key={shop.id} />
70
+ <ShopRow shop={shop} editShopUrl={this.props.editShopUrl} editSubscriptionUrl={this.props.editSubscriptionUrl} key={shop.id} />
70
71
  )
71
72
  } else {
72
73
  return null;
@@ -79,16 +80,13 @@ var ShopList = React.createClass({
79
80
  <table className="table-hover expanded">
80
81
  <thead>
81
82
  <tr>
82
- <th>ID</th>
83
- <th>Shopify Domain</th>
83
+ <th>Shop</th>
84
84
  <th>Status</th>
85
- <th>Email</th>
86
- <th>Country Name</th>
87
- <th>Currency</th>
88
- <th>Domain</th>
89
- <th>Plan</th>
90
- <th>Created</th>
91
- <th>Installed Duration</th>
85
+ <th>Country</th>
86
+ <th>Shopify Plan</th>
87
+ <th>Subscription</th>
88
+ <th>Source</th>
89
+ <th>Installed for</th>
92
90
  </tr>
93
91
  </thead>
94
92
  <tbody>
@@ -0,0 +1,43 @@
1
+ var ShopRow = (props) => {
2
+
3
+ var shop = props.shop,
4
+ editShopUrl = props.editShopUrl.replace(':id', shop.id),
5
+ domainName = shop.attributes['domain'],
6
+ countryName = shop.attributes['country-name'],
7
+ currency = shop.attributes['currency'],
8
+ planName = shop.attributes['plan-display-name'],
9
+ editSubscriptionUrl = props.editSubscriptionUrl
10
+ .replace(':shop_id', shop.id)
11
+ .replace(':id', shop.attributes['current-subscription-id']),
12
+ subscriptionPlan = shop.attributes['current-subscription-display-plan'],
13
+ subscriptionAmount = shop.attributes['current-subscription-display-amount'],
14
+ subscriptionSource = shop.attributes['current-subscription-source'],
15
+ installedDuration = shop.attributes['installed-duration'];
16
+
17
+ var subscriptionContent = null;
18
+ if(shop.attributes['current-subscription-id']) {
19
+ subscriptionContent = <a href={editSubscriptionUrl}>{subscriptionPlan} ({subscriptionAmount})</a>;
20
+ } else {
21
+ subscriptionContent = <span>{subscriptionPlan} ({subscriptionAmount})</span>;
22
+ }
23
+
24
+ var planCodeContent = null;
25
+ if(shop.attributes['current-subscription-display-plan-code']) {
26
+ planCodeContent = <span><br />{shop.attributes['current-subscription-display-plan-code']}</span>;
27
+ }
28
+
29
+ return (
30
+ <tr>
31
+ <td>
32
+ <a href={editShopUrl}>{domainName}</a><br />
33
+ <a href={'mailto:' + shop.attributes.email}>{shop.attributes.email}</a>
34
+ </td>
35
+ <td>{shop.attributes.status}</td>
36
+ <td>{countryName}</td>
37
+ <td>{planName}</td>
38
+ <td>{subscriptionContent}{planCodeContent}</td>
39
+ <td>{subscriptionSource}</td>
40
+ <td>{installedDuration}</td>
41
+ </tr>
42
+ )
43
+ };
@@ -0,0 +1,30 @@
1
+ const CardSection = ({ title, children, wrappable, borderless }) => {
2
+
3
+ const className = classNames({
4
+ 'next-card__section': true,
5
+ 'wrappable': wrappable,
6
+ 'wrappable--half-spacing': wrappable,
7
+ 'next-card__section--no-border': borderless,
8
+ 'next-card__section--no-top-spacing': borderless
9
+ });
10
+
11
+ const showTitle = () => {
12
+ if (title) {
13
+ return <CardSectionTitle title={title}/>;
14
+ } else {
15
+ return null;
16
+ }
17
+ };
18
+
19
+ return (
20
+ <div className={className}>
21
+ {showTitle()}
22
+ {children}
23
+ </div>
24
+ );
25
+ };
26
+
27
+ CardSection.propTypes = {
28
+ title: React.PropTypes.string,
29
+ children: React.PropTypes.node
30
+ };
@@ -0,0 +1,9 @@
1
+ const Card = ({ children }) => {
2
+ return (
3
+ <div className="next-card">{children}</div>
4
+ );
5
+ };
6
+
7
+ Card.PropTypes = {
8
+ children: React.PropTypes.node
9
+ };
@@ -0,0 +1,7 @@
1
+ const CardSectionTitle = ({ title }) => {
2
+ return <h3 className="next-heading">{title}</h3>;
3
+ };
4
+
5
+ CardSectionTitle.propTypes = {
6
+ title: React.PropTypes.string
7
+ };