@fleetbase/storefront-engine 0.3.18 → 0.3.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/addon/components/customer-panel/orders.hbs +107 -104
  2. package/addon/components/customer-panel/orders.js +52 -45
  3. package/addon/components/modals/incoming-order.hbs +208 -199
  4. package/addon/components/modals/order-ready-assign-driver.hbs +1 -1
  5. package/addon/components/order-panel/details.js +0 -2
  6. package/addon/components/order-panel.hbs +314 -1
  7. package/addon/components/order-panel.js +51 -3
  8. package/addon/components/widget/customers.hbs +75 -51
  9. package/addon/components/widget/customers.js +29 -41
  10. package/addon/components/widget/orders.hbs +278 -119
  11. package/addon/components/widget/orders.js +75 -80
  12. package/addon/components/widget/storefront-metrics.hbs +3 -6
  13. package/addon/components/widget/storefront-metrics.js +25 -41
  14. package/addon/controllers/orders/index.js +214 -105
  15. package/addon/controllers/settings/gateways.js +1 -1
  16. package/addon/helpers/get-tip-amount.js +13 -2
  17. package/addon/routes/application.js +2 -4
  18. package/addon/services/order-actions.js +248 -0
  19. package/addon/services/storefront.js +2 -0
  20. package/addon/styles/storefront-engine.css +48 -0
  21. package/addon/templates/home.hbs +2 -1
  22. package/addon/templates/orders/index/view.hbs +1 -1
  23. package/addon/templates/orders/index.hbs +26 -3
  24. package/addon/templates/products/index/index.hbs +28 -28
  25. package/addon/templates/settings.hbs +1 -1
  26. package/app/services/order-actions.js +1 -0
  27. package/composer.json +1 -1
  28. package/extension.json +1 -1
  29. package/package.json +1 -1
  30. package/server/migrations/2023_05_03_025307_create_carts_table.php +1 -1
  31. package/server/migrations/2023_05_03_025307_create_checkouts_table.php +1 -1
  32. package/server/migrations/2023_05_03_025307_create_gateways_table.php +1 -1
  33. package/server/migrations/2023_05_03_025307_create_network_stores_table.php +1 -1
  34. package/server/migrations/2023_05_03_025307_create_networks_table.php +1 -1
  35. package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +1 -1
  36. package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +1 -1
  37. package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +1 -1
  38. package/server/migrations/2023_05_03_025307_create_product_addons_table.php +1 -1
  39. package/server/migrations/2023_05_03_025307_create_product_hours_table.php +1 -1
  40. package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +1 -1
  41. package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +1 -1
  42. package/server/migrations/2023_05_03_025307_create_product_variants_table.php +1 -1
  43. package/server/migrations/2023_05_03_025307_create_products_table.php +1 -1
  44. package/server/migrations/2023_05_03_025307_create_reviews_table.php +1 -1
  45. package/server/migrations/2023_05_03_025307_create_store_hours_table.php +1 -1
  46. package/server/migrations/2023_05_03_025307_create_store_locations_table.php +1 -1
  47. package/server/migrations/2023_05_03_025307_create_stores_table.php +1 -1
  48. package/server/migrations/2023_05_03_025307_create_votes_table.php +1 -1
  49. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +1 -1
  50. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +1 -1
  51. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +1 -1
  52. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +1 -1
  53. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +1 -1
  54. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +1 -1
  55. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +1 -1
  56. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +1 -1
  57. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +1 -1
  58. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +1 -1
  59. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +1 -1
  60. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +1 -1
  61. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +1 -1
  62. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +1 -1
  63. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +1 -1
  64. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +1 -1
  65. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +1 -1
  66. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +1 -1
  67. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +1 -1
  68. package/server/src/Http/Controllers/ActionController.php +2 -1
  69. package/server/src/Http/Controllers/OrderController.php +15 -2
  70. package/server/src/Http/Controllers/v1/CheckoutController.php +11 -12
  71. package/server/src/Http/Controllers/v1/CustomerController.php +11 -3
  72. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +5 -5
  73. package/server/src/Http/Requests/CreateCustomerRequest.php +4 -0
  74. package/server/src/Http/Requests/InitializeCheckoutRequest.php +2 -1
  75. package/server/src/Models/Store.php +2 -2
  76. package/server/src/Observers/OrderObserver.php +7 -1
  77. package/server/src/Rules/IsValidLocation.php +2 -2
  78. package/server/src/Support/Storefront.php +34 -0
  79. package/translations/en-us.yaml +6 -1
@@ -1,18 +1,15 @@
1
- <div class="storefront-metrics-widget" {{did-insert this.setupWidget}} ...attributes>
1
+ <div class="storefront-metrics-widget" ...attributes>
2
2
  <div class="flex items-center justify-between">
3
3
  <div class="flex items-center">
4
4
  <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100 mr-2">
5
5
  {{t "storefront.component.widget.storefront-metrics.last-day"}}
6
6
  </h3>
7
- {{#if this.isLoading}}
7
+ {{#if this.loadMetrics.isRunning}}
8
8
  <div>
9
9
  <Spinner class="text-sky-400" />
10
10
  </div>
11
11
  {{/if}}
12
12
  </div>
13
- <div>
14
-
15
- </div>
16
13
  </div>
17
14
  <dl class="mt-4 grid grid-cols-2 gap-5 sm:grid-cols-4">
18
15
  <div class="px-4 py-5 border border-gray-200 dark:border-gray-900 bg-gray-50 dark:bg-gray-700 shadow-sm dark:shadow rounded-lg overflow-hidden sm:p-6">
@@ -35,7 +32,7 @@
35
32
 
36
33
  <div class="px-4 py-5 border border-gray-200 dark:border-gray-900 bg-gray-50 dark:bg-gray-700 shadow-sm dark:shadow rounded-lg overflow-hidden sm:p-6">
37
34
  <dt class="text-sm font-medium text-gray-500 dark:text-gray-100 truncate">
38
- {{t "storefront.common.orders"}}
35
+ {{pluralize this.metrics.stores_count (t "storefront.common.store") without-count=true}}
39
36
  </dt>
40
37
  <dd class="mt-1 text-3xl font-semibold text-gray-800 dark:text-white">
41
38
  {{this.metrics.stores_count}}
@@ -1,13 +1,17 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
- import { action, computed } from '@ember/object';
4
+ import { get } from '@ember/object';
5
+ import { debug } from '@ember/debug';
5
6
  import { startOfMonth, endOfMonth, format } from 'date-fns';
7
+ import { task } from 'ember-concurrency';
6
8
 
7
9
  export default class WidgetStorefrontMetricsComponent extends Component {
8
10
  @service fetch;
9
11
  @service storefront;
10
-
12
+ @tracked title = 'This Month';
13
+ @tracked start = format(startOfMonth(new Date()), 'yyyy-MM-dd');
14
+ @tracked end = format(endOfMonth(new Date()), 'yyyy-MM-dd');
11
15
  @tracked metrics = {
12
16
  orders_count: 0,
13
17
  customers_count: 0,
@@ -15,47 +19,27 @@ export default class WidgetStorefrontMetricsComponent extends Component {
15
19
  earnings_sum: 0,
16
20
  };
17
21
 
18
- @tracked isLoading = true;
19
- @tracked start = format(startOfMonth(new Date()), 'yyyy-MM-dd');
20
- @tracked end = format(endOfMonth(new Date()), 'yyyy-MM-dd');
21
-
22
- @computed('args.title') get title() {
23
- return this.args.title || 'This Month';
24
- }
25
-
26
- @action async setupWidget() {
27
- this.metrics = await this.fetchMetrics(this.start, this.end);
28
-
29
- this.storefront.on('order.broadcasted', this.reloadMetrics);
30
- this.storefront.on('storefront.changed', this.reloadMetrics);
31
- }
32
-
33
- @action async reloadMetrics() {
34
- this.metrics = await this.fetchMetrics(this.start, this.end);
22
+ constructor(owner, { title = 'This Month' }) {
23
+ super(...arguments);
24
+ this.title = title;
25
+ this.loadMetrics.perform(this.start, this.end);
26
+ this.storefront.on('order.broadcasted', () => {
27
+ this.loadMetrics.perform();
28
+ });
29
+ this.storefront.on('storefront.changed', () => {
30
+ this.loadMetrics.perform();
31
+ });
35
32
  }
36
33
 
37
- @action fetchMetrics(start, end) {
38
- this.isLoading = true;
34
+ @task *loadMetrics(start, end) {
35
+ const store = get(this.storefront, 'activeStore.id');
39
36
 
40
- return new Promise((resolve) => {
41
- const store = this.storefront?.activeStore?.id;
42
-
43
- if (!store) {
44
- this.isLoading = false;
45
- return resolve(this.metrics);
46
- }
47
-
48
- this.fetch
49
- .get('actions/metrics', { start, end, store }, { namespace: 'storefront/int/v1' })
50
- .then((metrics) => {
51
- this.isLoading = false;
52
- resolve(metrics);
53
- })
54
- .catch(() => {
55
- this.isLoading = false;
56
-
57
- resolve(this.metrics);
58
- });
59
- });
37
+ try {
38
+ const metrics = yield this.fetch.get('actions/metrics', { start, end, store }, { namespace: 'storefront/int/v1' });
39
+ this.metrics = metrics;
40
+ return metrics;
41
+ } catch (err) {
42
+ debug('Error loading storefront metrics:', err);
43
+ }
60
44
  }
61
45
  }
@@ -1,60 +1,39 @@
1
- import { action } from '@ember/object';
2
- import { inject as service } from '@ember/service';
3
- import { isBlank } from '@ember/utils';
4
1
  import BaseController from '@fleetbase/storefront-engine/controllers/base-controller';
5
2
  import { tracked } from '@glimmer/tracking';
6
- import { timeout } from 'ember-concurrency';
7
- import { task } from 'ember-concurrency-decorators';
3
+ import { action, get } from '@ember/object';
4
+ import { inject as service } from '@ember/service';
5
+ import { isBlank } from '@ember/utils';
6
+ import { isArray } from '@ember/array';
7
+ import { timeout, task } from 'ember-concurrency';
8
8
 
9
9
  export default class OrdersIndexController extends BaseController {
10
- /**
11
- * Inject the `notifications` service
12
- *
13
- * @var {Service}
14
- */
15
10
  @service notifications;
16
-
17
- /**
18
- * Inject the `intl` service
19
- *
20
- * @var {Service}
21
- */
22
11
  @service intl;
23
-
24
- /**
25
- * Inject the `modals-manager` service
26
- *
27
- * @var {Service}
28
- */
29
12
  @service modalsManager;
30
-
31
- /**
32
- * Inject the `crud` service
33
- *
34
- * @var {Service}
35
- */
36
13
  @service crud;
37
-
38
- /**
39
- * Inject the `fetch` service
40
- *
41
- * @var {Service}
42
- */
43
14
  @service fetch;
44
-
45
- /**
46
- * Inject the `filters` service
47
- *
48
- * @var {Service}
49
- */
50
15
  @service filters;
16
+ @service hostRouter;
17
+ @service storefront;
18
+ @tracked page = 1;
19
+ @tracked limit;
20
+ @tracked query;
21
+ @tracked sort = '-created_at';
22
+ @tracked public_id;
23
+ @tracked internal_id;
24
+ @tracked tracking;
25
+ @tracked facilitator;
26
+ @tracked customer;
27
+ @tracked driver;
28
+ @tracked payload;
29
+ @tracked pickup;
30
+ @tracked dropoff;
31
+ @tracked updated_by;
32
+ @tracked created_by;
33
+ @tracked status;
34
+ @tracked currency = 'USD';
51
35
 
52
- /**
53
- * Queryable parameters for this controller's model
54
- *
55
- * @var {Array}
56
- */
57
- queryParams = [
36
+ @tracked queryParams = [
58
37
  'page',
59
38
  'limit',
60
39
  'sort',
@@ -73,33 +52,17 @@ export default class OrdersIndexController extends BaseController {
73
52
  'status',
74
53
  ];
75
54
 
76
- @tracked page = 1;
77
- @tracked limit;
78
- @tracked query;
79
- @tracked sort = '-created_at';
80
- @tracked public_id;
81
- @tracked internal_id;
82
- @tracked tracking;
83
- @tracked facilitator;
84
- @tracked customer;
85
- @tracked driver;
86
- @tracked payload;
87
- @tracked pickup;
88
- @tracked dropoff;
89
- @tracked updated_by;
90
- @tracked created_by;
91
- @tracked status;
92
-
93
55
  @tracked columns = [
94
56
  {
95
57
  label: this.intl.t('storefront.common.id'),
96
58
  valuePath: 'public_id',
97
- width: '150px',
59
+ width: '130px',
98
60
  cellComponent: 'table/cell/anchor',
99
61
  onClick: this.viewOrder,
100
62
  resizable: true,
101
63
  sortable: true,
102
64
  filterable: true,
65
+ filterComponent: 'filter/string',
103
66
  },
104
67
  {
105
68
  label: this.intl.t('storefront.orders.index.internal-id'),
@@ -108,27 +71,38 @@ export default class OrdersIndexController extends BaseController {
108
71
  resizable: true,
109
72
  sortable: true,
110
73
  filterable: true,
74
+ hidden: true,
111
75
  filterComponent: 'filter/string',
112
76
  },
113
77
  {
114
78
  label: this.intl.t('storefront.orders.index.customer'),
115
79
  valuePath: 'customer.name',
116
80
  cellComponent: 'table/cell/base',
117
- width: '125px',
81
+ width: '100px',
118
82
  resizable: true,
119
83
  sortable: true,
120
- hidden: true,
84
+ hidden: false,
121
85
  filterable: true,
122
86
  filterComponent: 'filter/model',
123
87
  filterComponentPlaceholder: this.intl.t('storefront.orders.index.select-order-customer'),
124
88
  filterParam: 'customer',
125
89
  model: 'customer',
126
90
  },
91
+ {
92
+ label: this.intl.t('storefront.orders.index.total'),
93
+ cellComponent: 'table/cell/currency',
94
+ currency: this.currency,
95
+ valuePath: 'meta.total',
96
+ width: '100px',
97
+ resizable: true,
98
+ hidden: false,
99
+ sortable: true,
100
+ },
127
101
  {
128
102
  label: this.intl.t('storefront.common.pickup'),
129
103
  valuePath: 'pickupName',
130
104
  cellComponent: 'table/cell/base',
131
- width: '160px',
105
+ width: '150px',
132
106
  resizable: true,
133
107
  sortable: true,
134
108
  filterable: true,
@@ -141,7 +115,7 @@ export default class OrdersIndexController extends BaseController {
141
115
  label: this.intl.t('storefront.common.dropoff'),
142
116
  valuePath: 'dropoffName',
143
117
  cellComponent: 'table/cell/base',
144
- width: '160px',
118
+ width: '150px',
145
119
  resizable: true,
146
120
  sortable: true,
147
121
  filterable: true,
@@ -150,15 +124,34 @@ export default class OrdersIndexController extends BaseController {
150
124
  filterParam: 'dropoff',
151
125
  model: 'place',
152
126
  },
127
+ {
128
+ label: this.intl.t('storefront.orders.index.driver-assigned'),
129
+ cellComponent: 'table/cell/driver-name',
130
+ valuePath: 'driver_assigned',
131
+ modelPath: 'driver_assigned',
132
+ width: '150px',
133
+ resizable: true,
134
+ sortable: true,
135
+ filterable: true,
136
+ filterComponent: 'filter/model',
137
+ filterComponentPlaceholder: this.intl.t('storefront.orders.index.select-driver-for-order'),
138
+ filterParam: 'driver',
139
+ model: 'driver',
140
+ query: {
141
+ // no model, serializer, adapter for relations
142
+ without: ['fleets', 'vendor', 'vehicle', 'currentJob'],
143
+ },
144
+ },
153
145
  {
154
146
  label: this.intl.t('storefront.orders.index.scheduled-at'),
155
147
  valuePath: 'scheduledAt',
156
148
  sortParam: 'scheduled_at',
157
149
  filterParam: 'scheduled_at',
158
- width: '150px',
150
+ width: '125px',
159
151
  resizable: true,
160
152
  sortable: true,
161
153
  filterable: true,
154
+ hidden: true,
162
155
  filterComponent: 'filter/date',
163
156
  },
164
157
  {
@@ -169,43 +162,16 @@ export default class OrdersIndexController extends BaseController {
169
162
  hidden: true,
170
163
  width: '50px',
171
164
  },
172
- {
173
- label: this.intl.t('storefront.orders.index.transaction-total'),
174
- cellComponent: 'table/cell/base',
175
- valuePath: 'transaction_amount',
176
- width: '50px',
177
- resizable: true,
178
- hidden: true,
179
- sortable: true,
180
- },
181
165
  {
182
166
  label: this.intl.t('storefront.orders.index.tracking-number'),
183
167
  cellComponent: 'table/cell/base',
184
168
  valuePath: 'tracking_number.tracking_number',
185
- width: '170px',
169
+ width: '160px',
186
170
  resizable: true,
187
171
  sortable: true,
188
172
  filterable: true,
189
173
  filterComponent: 'filter/string',
190
174
  },
191
- {
192
- label: this.intl.t('storefront.orders.index.driver-assigned'),
193
- cellComponent: 'table/cell/driver-name',
194
- valuePath: 'driver_assigned',
195
- modelPath: 'driver_assigned',
196
- width: '170px',
197
- resizable: true,
198
- sortable: true,
199
- filterable: true,
200
- filterComponent: 'filter/model',
201
- filterComponentPlaceholder: this.intl.t('storefront.orders.index.select-driver-for-order'),
202
- filterParam: 'driver',
203
- model: 'driver',
204
- query: {
205
- // no model, serializer, adapter for relations
206
- without: ['fleets', 'vendor', 'vehicle', 'currentJob'],
207
- },
208
- },
209
175
  {
210
176
  label: this.intl.t('storefront.common.type'),
211
177
  cellComponent: 'cell/humanize',
@@ -219,7 +185,7 @@ export default class OrdersIndexController extends BaseController {
219
185
  label: this.intl.t('storefront.common.status'),
220
186
  valuePath: 'status',
221
187
  cellComponent: 'table/cell/status',
222
- width: '120px',
188
+ width: '140px',
223
189
  resizable: true,
224
190
  sortable: true,
225
191
  filterable: true,
@@ -228,10 +194,10 @@ export default class OrdersIndexController extends BaseController {
228
194
  },
229
195
  {
230
196
  label: this.intl.t('storefront.orders.index.created-at'),
231
- valuePath: 'createdAt',
197
+ valuePath: 'createdAtShort',
232
198
  sortParam: 'created_at',
233
199
  filterParam: 'created_at',
234
- width: '140px',
200
+ width: '100px',
235
201
  resizable: true,
236
202
  sortable: true,
237
203
  filterable: true,
@@ -239,7 +205,7 @@ export default class OrdersIndexController extends BaseController {
239
205
  },
240
206
  {
241
207
  label: this.intl.t('storefront.orders.index.updated-at'),
242
- valuePath: 'updatedAt',
208
+ valuePath: 'updatedAtShort',
243
209
  sortParam: 'updated_at',
244
210
  filterParam: 'updated_at',
245
211
  width: '125px',
@@ -282,25 +248,35 @@ export default class OrdersIndexController extends BaseController {
282
248
  ddMenuLabel: 'Order Actions',
283
249
  cellClassNames: 'overflow-visible',
284
250
  wrapperClass: 'flex items-center justify-end mx-2',
285
- width: '12%',
251
+ width: '90px',
286
252
  actions: [
287
253
  {
288
- label: this.intl.t('storefront.orders.index.view-order'),
254
+ label: this.intl.t('fleet-ops.operations.orders.index.view-order'),
289
255
  icon: 'eye',
290
256
  fn: this.viewOrder,
257
+ permission: 'fleet-ops view order',
258
+ },
259
+ {
260
+ label: this.intl.t('fleet-ops.operations.orders.index.dispatch-order'),
261
+ icon: 'paper-plane',
262
+ fn: this.dispatchOrder,
263
+ permission: 'fleet-ops dispatch order',
264
+ isVisible: (order) => order.canBeDispatched,
291
265
  },
292
266
  {
293
- label: this.intl.t('storefront.orders.index.cancel-order'),
267
+ label: this.intl.t('fleet-ops.operations.orders.index.cancel-order'),
294
268
  icon: 'ban',
295
269
  fn: this.cancelOrder,
270
+ permission: 'fleet-ops cancel order',
296
271
  },
297
272
  {
298
273
  separator: true,
299
274
  },
300
275
  {
301
- label: this.intl.t('storefront.orders.index.delete-order'),
276
+ label: this.intl.t('fleet-ops.operations.orders.index.delete-order'),
302
277
  icon: 'trash',
303
278
  fn: this.deleteOrder,
279
+ permission: 'fleet-ops delete order',
304
280
  },
305
281
  ],
306
282
  sortable: false,
@@ -310,6 +286,11 @@ export default class OrdersIndexController extends BaseController {
310
286
  },
311
287
  ];
312
288
 
289
+ constructor() {
290
+ super(...arguments);
291
+ this.currency = get(this.storefront, 'activeStore.currency');
292
+ }
293
+
313
294
  /**
314
295
  * The search task.
315
296
  *
@@ -337,4 +318,132 @@ export default class OrdersIndexController extends BaseController {
337
318
  @action viewOrder(order) {
338
319
  return this.transitionToRoute('orders.index.view', order);
339
320
  }
321
+
322
+ /**
323
+ * Cancels a specific order after confirmation.
324
+ * @param {Object} order - The order to cancel.
325
+ * @param {Object} [options={}] - Additional options for the modal.
326
+ * @action
327
+ * @memberof OperationsOrdersIndexController
328
+ */
329
+ @action cancelOrder(order, options = {}) {
330
+ this.modalsManager.confirm({
331
+ title: this.intl.t('fleet-ops.operations.orders.index.cancel-title'),
332
+ body: this.intl.t('fleet-ops.operations.orders.index.cancel-body'),
333
+ order,
334
+ confirm: async (modal) => {
335
+ modal.startLoading();
336
+
337
+ try {
338
+ await this.fetch.patch('orders/cancel', { order: order.id });
339
+ order.set('status', 'canceled');
340
+ this.notifications.success(this.intl.t('fleet-ops.operations.orders.index.cancel-success', { orderId: order.public_id }));
341
+ modal.done();
342
+ } catch (error) {
343
+ this.notifications.serverError(error);
344
+ modal.stopLoading();
345
+ }
346
+ },
347
+ ...options,
348
+ });
349
+ }
350
+
351
+ @action dispatchOrder(order, options = {}) {
352
+ this.modalsManager.confirm({
353
+ title: this.intl.t('fleet-ops.operations.orders.index.dispatch-title'),
354
+ body: this.intl.t('fleet-ops.operations.orders.index.dispatch-body'),
355
+ acceptButtonScheme: 'primary',
356
+ acceptButtonText: 'Dispatch',
357
+ acceptButtonIcon: 'paper-plane',
358
+ order,
359
+ confirm: async (modal) => {
360
+ modal.startLoading();
361
+
362
+ try {
363
+ await this.fetch.patch('orders/dispatch', { order: order.id });
364
+ order.set('status', 'dispatched');
365
+ this.notifications.success(this.intl.t('fleet-ops.operations.orders.index.dispatch-success', { orderId: order.public_id }));
366
+ modal.done();
367
+ } catch (error) {
368
+ this.notifications.serverError(error);
369
+ modal.stopLoading();
370
+ }
371
+ },
372
+ ...options,
373
+ });
374
+ }
375
+
376
+ @action deleteOrder(order, options = {}) {
377
+ this.crud.delete(order, {
378
+ onSuccess: () => {
379
+ return this.hostRouter.refresh();
380
+ },
381
+ ...options,
382
+ });
383
+ }
384
+
385
+ @action bulkDeleteOrders(selected = []) {
386
+ selected = selected.length > 0 ? selected : this.table.selectedRows;
387
+
388
+ this.crud.bulkDelete(selected, {
389
+ modelNamePath: `public_id`,
390
+ acceptButtonText: 'Delete Orders',
391
+ onSuccess: async () => {
392
+ await this.hostRouter.refresh();
393
+ this.table.untoggleSelectAll();
394
+ },
395
+ });
396
+ }
397
+
398
+ @action bulkCancelOrders(selected = []) {
399
+ selected = selected.length > 0 ? selected : this.table.selectedRows;
400
+
401
+ if (!isArray(selected) || selected.length === 0) {
402
+ return;
403
+ }
404
+
405
+ this.crud.bulkAction('cancel', selected, {
406
+ acceptButtonText: 'Cancel Orders',
407
+ acceptButtonScheme: 'danger',
408
+ acceptButtonIcon: 'ban',
409
+ modelNamePath: `public_id`,
410
+ actionPath: `orders/bulk-cancel`,
411
+ actionMethod: `PATCH`,
412
+ onConfirm: (canceledOrders) => {
413
+ canceledOrders.forEach((order) => {
414
+ order.set('status', 'canceled');
415
+ });
416
+ },
417
+ onSuccess: async () => {
418
+ await this.hostRouter.refresh();
419
+ this.table.untoggleSelectAll();
420
+ },
421
+ });
422
+ }
423
+
424
+ @action bulkDispatchOrders(selected = []) {
425
+ selected = selected.length > 0 ? selected : this.table.selectedRows;
426
+
427
+ if (!isArray(selected) || selected.length === 0) {
428
+ return;
429
+ }
430
+
431
+ this.crud.bulkAction('dispatch', selected, {
432
+ acceptButtonText: 'Dispatch Orders',
433
+ acceptButtonScheme: 'magic',
434
+ acceptButtonIcon: 'rocket',
435
+ modelNamePath: 'public_id',
436
+ actionPath: 'orders/bulk-dispatch',
437
+ actionMethod: 'POST',
438
+ onConfirm: (dispatchedOrders) => {
439
+ dispatchedOrders.forEach((order) => {
440
+ order.set('status', 'dispatched');
441
+ });
442
+ },
443
+ onSuccess: async () => {
444
+ await this.hostRouter.refresh();
445
+ this.table.untoggleSelectAll();
446
+ },
447
+ });
448
+ }
340
449
  }
@@ -71,7 +71,7 @@ export default class SettingsGatewaysController extends Controller {
71
71
 
72
72
  set(gateway.config, key, value);
73
73
  },
74
- confirm: async (modal, done) => {
74
+ confirm: async (modal) => {
75
75
  modal.startLoading();
76
76
 
77
77
  try {
@@ -1,5 +1,16 @@
1
1
  import { helper } from '@ember/component/helper';
2
+ import formatCurrency from '@fleetbase/ember-ui/utils/format-currency';
3
+ import numbersOnly from '@fleetbase/ember-ui/utils/numbers-only';
4
+ import calculatePercentage from '@fleetbase/ember-core/utils/calculate-percentage';
2
5
 
3
- export default helper(function getTipAmount(positional /*, named*/) {
4
- return positional;
6
+ export default helper(function getTipAmount([tip, subtotal, currency]) {
7
+ let amount = tip;
8
+ if (typeof tip === 'string' && tip.endsWith('%')) {
9
+ amount = calculatePercentage(numbersOnly(tip), subtotal);
10
+ }
11
+
12
+ amount = parseInt(amount);
13
+ amount = isNaN(amount) ? 0 : amount;
14
+
15
+ return formatCurrency(amount, currency);
5
16
  });
@@ -45,10 +45,8 @@ export default class ApplicationRoute extends Route {
45
45
  return this.store.query('store', { limit: 300, sort: '-updated_at' });
46
46
  }
47
47
 
48
- afterModel(model) {
49
- if (model.length) {
50
- // this.storefront.listenForIncomingOrders();
51
- }
48
+ afterModel() {
49
+ this.storefront.listenForIncomingOrders();
52
50
  }
53
51
 
54
52
  disableSandbox() {