@fleetbase/storefront-engine 0.3.17 → 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 (102) 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/manage-addons.js +6 -2
  5. package/addon/components/modals/order-ready-assign-driver.hbs +1 -1
  6. package/addon/components/order-panel/details.js +0 -2
  7. package/addon/components/order-panel.hbs +314 -1
  8. package/addon/components/order-panel.js +51 -3
  9. package/addon/components/widget/customers.hbs +75 -51
  10. package/addon/components/widget/customers.js +29 -41
  11. package/addon/components/widget/orders.hbs +278 -119
  12. package/addon/components/widget/orders.js +75 -80
  13. package/addon/components/widget/storefront-metrics.hbs +3 -6
  14. package/addon/components/widget/storefront-metrics.js +25 -41
  15. package/addon/controllers/orders/index.js +214 -105
  16. package/addon/controllers/products/index/category/new.js +2 -1
  17. package/addon/controllers/products/index/index.js +0 -23
  18. package/addon/controllers/settings/gateways.js +14 -18
  19. package/addon/helpers/get-tip-amount.js +13 -2
  20. package/addon/models/product-addon-category.js +3 -0
  21. package/addon/routes/application.js +2 -4
  22. package/addon/services/order-actions.js +248 -0
  23. package/addon/services/storefront.js +2 -0
  24. package/addon/styles/storefront-engine.css +55 -0
  25. package/addon/templates/home.hbs +2 -1
  26. package/addon/templates/orders/index/view.hbs +1 -1
  27. package/addon/templates/orders/index.hbs +26 -3
  28. package/addon/templates/products/index/category/new.hbs +4 -1
  29. package/addon/templates/products/index/index.hbs +28 -28
  30. package/addon/templates/settings/gateways.hbs +1 -1
  31. package/addon/templates/settings/index.hbs +2 -2
  32. package/addon/templates/settings.hbs +1 -1
  33. package/app/services/order-actions.js +1 -0
  34. package/composer.json +1 -1
  35. package/extension.json +1 -1
  36. package/package.json +1 -1
  37. package/server/migrations/2023_05_03_025307_create_carts_table.php +1 -1
  38. package/server/migrations/2023_05_03_025307_create_checkouts_table.php +1 -1
  39. package/server/migrations/2023_05_03_025307_create_gateways_table.php +1 -1
  40. package/server/migrations/2023_05_03_025307_create_network_stores_table.php +1 -1
  41. package/server/migrations/2023_05_03_025307_create_networks_table.php +1 -1
  42. package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +1 -1
  43. package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +1 -1
  44. package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +1 -1
  45. package/server/migrations/2023_05_03_025307_create_product_addons_table.php +1 -1
  46. package/server/migrations/2023_05_03_025307_create_product_hours_table.php +1 -1
  47. package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +1 -1
  48. package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +1 -1
  49. package/server/migrations/2023_05_03_025307_create_product_variants_table.php +1 -1
  50. package/server/migrations/2023_05_03_025307_create_products_table.php +1 -1
  51. package/server/migrations/2023_05_03_025307_create_reviews_table.php +1 -1
  52. package/server/migrations/2023_05_03_025307_create_store_hours_table.php +1 -1
  53. package/server/migrations/2023_05_03_025307_create_store_locations_table.php +1 -1
  54. package/server/migrations/2023_05_03_025307_create_stores_table.php +1 -1
  55. package/server/migrations/2023_05_03_025307_create_votes_table.php +1 -1
  56. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +1 -1
  57. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +1 -1
  58. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +1 -1
  59. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +1 -1
  60. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +1 -1
  61. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +1 -1
  62. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +1 -1
  63. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +1 -1
  64. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +1 -1
  65. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +1 -1
  66. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +1 -1
  67. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +1 -1
  68. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +1 -1
  69. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +1 -1
  70. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +1 -1
  71. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +1 -1
  72. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +1 -1
  73. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +1 -1
  74. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +1 -1
  75. package/server/src/Http/Controllers/ActionController.php +2 -1
  76. package/server/src/Http/Controllers/OrderController.php +15 -2
  77. package/server/src/Http/Controllers/ProductController.php +2 -0
  78. package/server/src/Http/Controllers/v1/CheckoutController.php +337 -40
  79. package/server/src/Http/Controllers/v1/CustomerController.php +88 -3
  80. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +5 -5
  81. package/server/src/Http/Controllers/v1/StoreController.php +35 -5
  82. package/server/src/Http/Requests/CreateCustomerRequest.php +4 -0
  83. package/server/src/Http/Requests/CreateStripeSetupIntentRequest.php +31 -0
  84. package/server/src/Http/Requests/CustomerRequest.php +31 -0
  85. package/server/src/Http/Requests/InitializeCheckoutRequest.php +2 -1
  86. package/server/src/Http/Resources/Cart.php +18 -1
  87. package/server/src/Http/Resources/Customer.php +19 -14
  88. package/server/src/Http/Resources/Store.php +5 -1
  89. package/server/src/Models/AddonCategory.php +14 -16
  90. package/server/src/Models/Cart.php +10 -5
  91. package/server/src/Models/Customer.php +2 -2
  92. package/server/src/Models/Gateway.php +9 -4
  93. package/server/src/Models/Product.php +9 -10
  94. package/server/src/Models/ProductAddonCategory.php +2 -0
  95. package/server/src/Models/Store.php +2 -2
  96. package/server/src/Observers/OrderObserver.php +7 -1
  97. package/server/src/Rules/IsValidLocation.php +2 -2
  98. package/server/src/Support/QPay.php +35 -1
  99. package/server/src/Support/Storefront.php +34 -0
  100. package/server/src/Support/StripeUtils.php +38 -0
  101. package/server/src/routes.php +19 -0
  102. package/translations/en-us.yaml +8 -2
@@ -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
  });
@@ -12,6 +12,8 @@ export default class ProductAddonCategoryModel extends Model {
12
12
 
13
13
  /** @attributes */
14
14
  @attr('string') name;
15
+ @attr('number') max_selectable;
16
+ @attr('boolean') is_required;
15
17
  @attr('raw') excluded_addons;
16
18
 
17
19
  /** @dates */
@@ -25,6 +27,7 @@ export default class ProductAddonCategoryModel extends Model {
25
27
  category_uuid: this.category_uuid,
26
28
  product_uuid: this.product_uuid,
27
29
  name: this.name,
30
+ max_selectable: this.max_selectable,
28
31
  excluded_addons: getWithDefault(this, 'excluded_addons', []),
29
32
  updated_at: this.updated_at,
30
33
  created_at: this.created_at,
@@ -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() {
@@ -0,0 +1,248 @@
1
+ import Service, { inject as service } from '@ember/service';
2
+ import toBoolean from '@fleetbase/ember-core/utils/to-boolean';
3
+
4
+ export default class OrderActionsService extends Service {
5
+ @service intl;
6
+ @service notifications;
7
+ @service fetch;
8
+ @service store;
9
+ @service modalsManager;
10
+ @service storefront;
11
+
12
+ cancelOrder(order, callback) {
13
+ this.modalsManager.confirm({
14
+ title: this.intl.t('fleet-ops.operations.orders.index.cancel-title'),
15
+ body: this.intl.t('fleet-ops.operations.orders.index.cancel-body'),
16
+ order,
17
+ confirm: async (modal) => {
18
+ modal.startLoading();
19
+
20
+ try {
21
+ await this.fetch.patch('orders/cancel', { order: order.id });
22
+ order.set('status', 'canceled');
23
+ this.notifications.success('Order canceled.');
24
+ modal.done();
25
+ if (typeof callback === 'function') {
26
+ callback(order);
27
+ }
28
+ } catch (error) {
29
+ this.notifications.serverError(error);
30
+ } finally {
31
+ modal.stopLoading();
32
+ }
33
+ },
34
+ decline: async (modal) => {
35
+ if (typeof callback === 'function') {
36
+ callback(order);
37
+ }
38
+ modal.done();
39
+ },
40
+ });
41
+ }
42
+
43
+ async assignDriver(order, callback) {
44
+ await order.loadDriver();
45
+
46
+ this.modalsManager.show('modals/assign-driver', {
47
+ title: this.intl.t('storefront.component.widget.orders.assign-driver-modal-title'),
48
+ acceptButtonText: this.intl.t('storefront.component.widget.orders.assign-driver-modal-accept-button-text'),
49
+ acceptButtonScheme: 'success',
50
+ acceptButtonIcon: 'check',
51
+ driver: order.driver_assigned,
52
+ order,
53
+ confirm: async (modal) => {
54
+ modal.startLoading();
55
+
56
+ try {
57
+ await order.save();
58
+ this.notifications.success('Driver assigned to order.');
59
+ modal.done();
60
+ if (typeof callback === 'function') {
61
+ callback(order);
62
+ }
63
+ } catch (err) {
64
+ this.notifications.serverError(err);
65
+ } finally {
66
+ modal.stopLoading();
67
+ }
68
+ },
69
+ decline: async (modal) => {
70
+ if (typeof callback === 'function') {
71
+ callback(order);
72
+ }
73
+ modal.done();
74
+ },
75
+ });
76
+ }
77
+
78
+ async acceptOrder(order, callback) {
79
+ const store = this.storefront.activeStore;
80
+
81
+ await order.loadPayload();
82
+ await order.loadCustomer();
83
+
84
+ this.modalsManager.show('modals/incoming-order', {
85
+ title: this.intl.t('storefront.component.widget.orders.accept-order'),
86
+ acceptButtonText: this.intl.t('storefront.component.widget.orders.accept-order'),
87
+ acceptButtonScheme: 'success',
88
+ acceptButtonIcon: 'check',
89
+ modalClass: 'scrollable-height-dialog',
90
+ order,
91
+ store,
92
+ assignDriver: async () => {
93
+ await this.modalsManager.done();
94
+ this.assignDriver(order, (order) => {
95
+ this.acceptOrder(order);
96
+ });
97
+ },
98
+ confirm: async (modal) => {
99
+ modal.startLoading();
100
+
101
+ try {
102
+ await this.fetch.post('orders/accept', { order: order.id }, { namespace: 'storefront/int/v1' });
103
+ modal.done();
104
+ if (typeof callback === 'function') {
105
+ callback(order);
106
+ }
107
+ } catch (err) {
108
+ this.notifications.serverError(err);
109
+ } finally {
110
+ modal.stopLoading();
111
+ }
112
+ },
113
+ decline: async (modal) => {
114
+ if (typeof callback === 'function') {
115
+ callback(order);
116
+ }
117
+ modal.done();
118
+ },
119
+ });
120
+ }
121
+
122
+ markAsReady(order, callback) {
123
+ // for pickup orders
124
+ if (order.meta && toBoolean(order.meta.is_pickup) === true) {
125
+ return this.modalsManager.confirm({
126
+ title: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-pickup-title'),
127
+ body: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-pickup-body'),
128
+ acceptButtonText: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-pickup-accept-button-text'),
129
+ acceptButtonIcon: 'check',
130
+ acceptButtonScheme: 'success',
131
+ confirm: async (modal) => {
132
+ modal.startLoading();
133
+
134
+ try {
135
+ await this.fetch.post('orders/ready', { order: order.id }, { namespace: 'storefront/int/v1' });
136
+ modal.done();
137
+ if (typeof callback === 'function') {
138
+ callback(order);
139
+ }
140
+ } catch (err) {
141
+ this.notifications.serverError(err);
142
+ } finally {
143
+ modal.stopLoading();
144
+ }
145
+ },
146
+ decline: async (modal) => {
147
+ if (typeof callback === 'function') {
148
+ callback(order);
149
+ }
150
+ modal.done();
151
+ },
152
+ });
153
+ }
154
+
155
+ if (!order.adhoc) {
156
+ // prompt to assign driver then dispatch
157
+ return this.modalsManager.show('modals/order-ready-assign-driver', {
158
+ title: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-not-adhoc-title'),
159
+ acceptButtonText: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-not-adhoc-accept-button-text'),
160
+ acceptButtonScheme: 'success',
161
+ acceptButtonIcon: 'check',
162
+ adhoc: false,
163
+ driver: null,
164
+ order,
165
+ confirm: async (modal) => {
166
+ modal.startLoading();
167
+
168
+ try {
169
+ await this.fetch.post('orders/ready', { order: order.id, driver: modal.getOption('driver.id'), adhoc: modal.getOption('adhoc') }, { namespace: 'storefront/int/v1' });
170
+ modal.done();
171
+ if (typeof callback === 'function') {
172
+ callback(order);
173
+ }
174
+ } catch (err) {
175
+ this.notifications.serverError(err);
176
+ } finally {
177
+ modal.stopLoading();
178
+ }
179
+ },
180
+ decline: async (modal) => {
181
+ if (typeof callback === 'function') {
182
+ callback(order);
183
+ }
184
+ modal.done();
185
+ },
186
+ });
187
+ }
188
+
189
+ this.modalsManager.confirm({
190
+ title: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-title'),
191
+ body: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-body'),
192
+ acceptButtonText: this.intl.t('storefront.component.widget.orders.mark-as-ready-modal-accept-button-text'),
193
+ acceptButtonIcon: 'check',
194
+ acceptButtonScheme: 'success',
195
+ confirm: async (modal) => {
196
+ modal.startLoading();
197
+ try {
198
+ await this.fetch.post('orders/ready', { order: order.id }, { namespace: 'storefront/int/v1' });
199
+ modal.done();
200
+ if (typeof callback === 'function') {
201
+ callback(order);
202
+ }
203
+ } catch (err) {
204
+ this.notifications.serverError(err);
205
+ } finally {
206
+ modal.stopLoading();
207
+ }
208
+ },
209
+ decline: async (modal) => {
210
+ if (typeof callback === 'function') {
211
+ callback(order);
212
+ }
213
+ modal.done();
214
+ },
215
+ });
216
+ }
217
+
218
+ markAsCompleted(order, callback) {
219
+ this.modalsManager.confirm({
220
+ title: this.intl.t('storefront.component.widget.orders.mark-as-completed-modal-title'),
221
+ body: this.intl.t('storefront.component.widget.orders.mark-as-completed-modal-body'),
222
+ acceptButtonText: this.intl.t('storefront.component.widget.orders.mark-as-completed-accept-button-text'),
223
+ acceptButtonIcon: 'check',
224
+ acceptButtonScheme: 'success',
225
+ confirm: async (modal) => {
226
+ modal.startLoading();
227
+
228
+ try {
229
+ await this.fetch.post('orders/completed', { order: order.id }, { namespace: 'storefront/int/v1' });
230
+ modal.done();
231
+ if (typeof callback === 'function') {
232
+ callback(order);
233
+ }
234
+ } catch (err) {
235
+ this.notifications.serverError(err);
236
+ } finally {
237
+ modal.stopLoading();
238
+ }
239
+ },
240
+ decline: async (modal) => {
241
+ if (typeof callback === 'function') {
242
+ callback(order);
243
+ }
244
+ modal.done();
245
+ },
246
+ });
247
+ }
248
+ }
@@ -15,6 +15,7 @@ export default class StorefrontService extends Service.extend(Evented) {
15
15
  @service modalsManager;
16
16
  @service hostRouter;
17
17
  @service abilities;
18
+ @service socket;
18
19
 
19
20
  /**
20
21
  * Gets the active store.
@@ -102,6 +103,7 @@ export default class StorefrontService extends Service.extend(Evented) {
102
103
  declineButtonScheme: 'danger',
103
104
  closeButton: false,
104
105
  backdropClose: false,
106
+ modalClass: 'scrollable-height-dialog',
105
107
  order,
106
108
  store,
107
109
  confirm: async (modal) => {
@@ -4,3 +4,58 @@
4
4
  .ui-combo-box .selected-list a.combo-box-option.selected:hover {
5
5
  background-color: #76a9fa !important;
6
6
  }
7
+
8
+ /** hotfix tag input */
9
+ .emberTagInput.form-input {
10
+ flex-wrap: wrap !important;
11
+ gap: 0.35rem !important;
12
+ height: auto !important;
13
+ }
14
+
15
+ .scrollable-height-dialog {
16
+ display: flex !important;
17
+ padding: 0;
18
+ min-width: 100%;
19
+ min-height: 100%;
20
+ height: 100%;
21
+ width: 100%;
22
+ margin: 0;
23
+ align-items: center;
24
+ justify-content: center;
25
+ }
26
+
27
+ .scrollable-height-dialog > .flb--modal-dialog {
28
+ padding: 25rem 0 5rem;
29
+ height: 100%;
30
+ overflow-y: scroll;
31
+ width: 100%;
32
+ margin: 1rem 0 0;
33
+ min-width: 100%;
34
+ min-height: 100%;
35
+ }
36
+
37
+ .storefront-widget-table {
38
+ table-layout: fixed;
39
+ width: 100%;
40
+ max-width: 100%;
41
+ }
42
+
43
+ .status-badge.pickup-ready-status-badge > span {
44
+ background-color: #166534;
45
+ border: 1px solid #15803d;
46
+ color: #dcfce7;
47
+ }
48
+
49
+ .status-badge.pickup-ready-status-badge > span svg {
50
+ color: #86efac;
51
+ }
52
+
53
+ .status-badge.order-canceled-status-badge > span {
54
+ background-color: #991b1b;
55
+ border: 1px solid #b91c1c;
56
+ color: #fee2e2;
57
+ }
58
+
59
+ .status-badge.order-canceled-status-badge > span svg {
60
+ color: #fca5a5;
61
+ }
@@ -1,7 +1,8 @@
1
1
  <Layout::Section::Header @title={{t "storefront.home.title"}} @hideActions={{true}} />
2
2
 
3
- <Layout::Section::Body class="space-y-4" @padded={{true}}>
3
+ <Layout::Section::Body class="space-y-4 h-full overflow-y-scroll" @padded={{true}}>
4
4
  <Widget::StorefrontMetrics />
5
5
  <Widget::Orders />
6
6
  <Widget::Customers />
7
+ <Spacer @height="300px" />
7
8
  </Layout::Section::Body>
@@ -1 +1 @@
1
- <OrderPanel::Details @order={{@model}} @onPressCancel={{this.transitionBack}} />
1
+ <OrderPanel @context={{@model}} @onPressCancel={{this.transitionBack}} />
@@ -9,11 +9,34 @@
9
9
  />
10
10
  <VisibleColumnPicker @columns={{this.columns}} @onChange={{fn (mut this.columns)}} class="mr-2" />
11
11
  {{#if (safe-has this.table "selectedRows")}}
12
- <DropdownButton @icon="layer-group" @text={{t "storefront.common.bulk-action"}} @type="magic" @size="sm" @buttonWrapperClass="mr-2" @contentClass="dropdown-menu" @permission="storefront delete order" as |dd|>
12
+ <DropdownButton
13
+ @icon="layer-group"
14
+ @text={{t "storefront.common.bulk-action"}}
15
+ @type="magic"
16
+ @size="sm"
17
+ @buttonWrapperClass="mr-2"
18
+ @contentClass="dropdown-menu"
19
+ @permission="storefront delete order"
20
+ as |dd|
21
+ >
13
22
  <div class="next-dd-menu mt-2 mx-0">
14
23
  <div class="px-1">
15
- <a href="#" class="text-red-500 next-dd-item" {{on "click" (dropdown-fn dd this.bulkDeleteOrders)}}>
16
- {{t "storefront.orders.index.delete-order"}}
24
+ <a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.bulkCancelOrders)}} disabled={{cannot "fleet-ops cancel order"}}>
25
+ <div class="w-6"><FaIcon @icon="ban" @size="sm" /></div>
26
+ <div>{{t "fleet-ops.operations.orders.index.cancel-orders"}}</div>
27
+ </a>
28
+ </div>
29
+ <div class="px-1">
30
+ <a href="javascript:;" class="text-red-500 next-dd-item" {{on "click" (dropdown-fn dd this.bulkDeleteOrders)}} disabled={{cannot "fleet-ops delete order"}}>
31
+ <div class="w-6"><FaIcon @icon="trash" @size="sm" /></div>
32
+ <div>{{t "fleet-ops.operations.orders.index.delete-orders"}}</div>
33
+ </a>
34
+ </div>
35
+ <div class="next-dd-menu-seperator"></div>
36
+ <div class="px-1">
37
+ <a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.bulkDispatchOrders)}} disabled={{cannot "fleet-ops dispatch order"}}>
38
+ <div class="w-6"><FaIcon @icon="rocket" @size="sm" /></div>
39
+ <div>{{t "fleet-ops.operations.orders.index.dispatch-orders"}}</div>
17
40
  </a>
18
41
  </div>
19
42
  </div>
@@ -23,6 +23,9 @@
23
23
  <InputGroup @name="Product Description" @helpText="Enter a description of your product">
24
24
  <Textarea @value={{this.product.description}} class="form-input w-full" placeholder="Enter a description of your product...." disabled={{unauthorized}} rows={{4}} />
25
25
  </InputGroup>
26
+ <InputGroup @name="Product Status">
27
+ <Select class="w-full" @value={{this.product.status}} @placeholder="Select product status" @options={{this.statusOptions}} @onSelect={{fn (mut this.product.status)}} @humanize={{true}} />
28
+ </InputGroup>
26
29
  <InputGroup @name="Product Tags">
27
30
  <TagInput
28
31
  class="form-input"
@@ -261,7 +264,7 @@
261
264
  {{addon.description}}
262
265
  </div>
263
266
  <div class="col-span-2">
264
- {{format-currency addon.price}}
267
+ {{format-currency addon.price this.product.currency}}
265
268
  </div>
266
269
  </div>
267
270
  {{/each}}
@@ -1,36 +1,36 @@
1
1
  <Layout::Section::Header @title="All Products" @searchQuery={{this.query}} @onSearch={{perform this.search}} />
2
2
 
3
- <Layout::Section::Body>
4
- <div class="h-screen overflow-y-scroll p-6">
5
- <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
6
- {{#each @model as |product|}}
7
- <div class="border border-gray-200 bg-white dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700 text-center rounded-md px-2 py-3">
8
- <div class="flex flex-col items-center justify-center overflow-hidden">
9
- <div class="mb-3 flex items-center justify-center">
10
- <img src={{product.primary_image_url}} alt={{product.name}} class="w-24 h-24" />
11
- </div>
12
- <h4 class="font-semibold mb-1">{{product.name}}</h4>
13
- <p class="text-sm truncate">{{product.description}}</p>
14
- <p class="mb-2 text-sm text-green-400">{{format-currency product.price product.currency}}</p>
15
- <div class="flex items-center justify-evenly space-x-4">
16
- <LinkTo @route="products.index.index.edit" @model={{product}} disabled={{cannot "storefront update product"}} class="text-sm">
17
- <FaIcon @icon="pencil" class="mr-1" />
18
- <span class="destroy-action">Edit</span>
19
- </LinkTo>
20
- <a href="javascript:;" class="destroy-action text-sm" disabled={{cannot "storefront delete product"}} {{on "click" (fn this.deleteProduct product)}}>
21
- <FaIcon @icon="trash" class="text-red-500 mr-1" />
22
- <span class="destroy-action">Delete</span>
23
- </a>
24
- </div>
3
+ <Layout::Section::Body class="h-full overflow-y-scroll p-6">
4
+
5
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
6
+ {{#each @model as |product|}}
7
+ <div class="border border-gray-200 bg-white dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700 text-center rounded-md px-2 py-3">
8
+ <div class="flex flex-col items-center justify-center overflow-hidden">
9
+ <div class="mb-3 flex items-center justify-center">
10
+ <img src={{product.primary_image_url}} alt={{product.name}} class="w-24 h-24" />
11
+ </div>
12
+ <h4 class="font-semibold mb-1">{{product.name}}</h4>
13
+ <p class="text-sm truncate">{{product.description}}</p>
14
+ <p class="mb-2 text-sm text-green-400">{{format-currency product.price product.currency}}</p>
15
+ <div class="flex items-center justify-evenly space-x-4">
16
+ <LinkTo @route="products.index.index.edit" @model={{product}} disabled={{cannot "storefront update product"}} class="text-sm">
17
+ <FaIcon @icon="pencil" class="mr-1" />
18
+ <span class="destroy-action">Edit</span>
19
+ </LinkTo>
20
+ <a href="javascript:;" class="destroy-action text-sm" disabled={{cannot "storefront delete product"}} {{on "click" (fn this.deleteProduct product)}}>
21
+ <FaIcon @icon="trash" class="text-red-500 mr-1" />
22
+ <span class="destroy-action">Delete</span>
23
+ </a>
25
24
  </div>
26
25
  </div>
27
- {{else}}
28
- <div>
29
- <h3 class="dark:text-gray-100 text-opacity-75 text-sm">No products</h3>
30
- </div>
31
- {{/each}}
32
- </div>
26
+ </div>
27
+ {{else}}
28
+ <div>
29
+ <h3 class="dark:text-gray-100 text-opacity-75 text-sm">No products</h3>
30
+ </div>
31
+ {{/each}}
33
32
  </div>
33
+ <Spacer @height="400px" />
34
34
  </Layout::Section::Body>
35
35
 
36
36
  <Layout::Section::Footer>
@@ -15,7 +15,7 @@
15
15
  </div>
16
16
  </div>
17
17
 
18
- {{#each this.model as |gateway|}}
18
+ {{#each @model as |gateway|}}
19
19
  <ContentPanel @title={{gateway.name}} @open={{true}} @pad={{true}}>
20
20
  <InputGroup @name={{t "storefront.settings.gateways.gateway-name"}} @value={{gateway.name}} @helpText={{t "storefront.settings.gateways.helpText"}} />
21
21
  <InputGroup
@@ -293,14 +293,14 @@
293
293
  <div class="input-group">
294
294
  <Toggle @isToggled={{@model.options.tips_enabled}} @onToggle={{fn (mut @model.options.tips_enabled)}}>
295
295
  <FaIcon @icon="cash-register" class="text-gray-600 dark:text-gray-400 mx-2" /><span class="dark:text-gray-100 text-sm">{{t
296
- "storefront.settings.index.enable-tip"
296
+ "storefront.settings.index.enable-tips"
297
297
  }}</span>
298
298
  </Toggle>
299
299
  </div>
300
300
  <div class="input-group">
301
301
  <Toggle @isToggled={{@model.options.delivery_tips_enabled}} @onToggle={{fn (mut @model.options.delivery_tips_enabled)}}>
302
302
  <FaIcon @icon="face-smile" class="text-gray-600 dark:text-gray-400 mx-2" /><span class="dark:text-gray-100 text-sm">{{t
303
- "storefront.settings.index.enable-integrated-vendors"
303
+ "storefront.settings.index.enable-delivery-tips"
304
304
  }}</span>
305
305
  </Toggle>
306
306
  </div>
@@ -18,7 +18,7 @@
18
18
  <span>{{t "storefront.common.api"}}</span>
19
19
  </LinkTo>
20
20
  <LinkTo @route="settings.notifications" class="ui-tab">
21
- <FaIcon @icon="bells" class="mr-1" />
21
+ <FaIcon @icon="bell-concierge" class="mr-1" />
22
22
  <span>{{t "storefront.common.notification"}}</span>
23
23
  </LinkTo>
24
24
  </nav>
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/storefront-engine/services/order-actions';
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/storefront-api",
3
- "version": "0.3.17",
3
+ "version": "0.3.20",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Storefront",
3
- "version": "0.3.17",
3
+ "version": "0.3.20",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/storefront",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/storefront-engine",
3
- "version": "0.3.17",
3
+ "version": "0.3.20",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "storefront",
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4
4
  use Illuminate\Database\Schema\Blueprint;
5
5
  use Illuminate\Support\Facades\Schema;
6
6
 
7
- return new class() extends Migration {
7
+ return new class extends Migration {
8
8
  /**
9
9
  * Run the migrations.
10
10
  *
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4
4
  use Illuminate\Database\Schema\Blueprint;
5
5
  use Illuminate\Support\Facades\Schema;
6
6
 
7
- return new class() extends Migration {
7
+ return new class extends Migration {
8
8
  /**
9
9
  * Run the migrations.
10
10
  *
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4
4
  use Illuminate\Database\Schema\Blueprint;
5
5
  use Illuminate\Support\Facades\Schema;
6
6
 
7
- return new class() extends Migration {
7
+ return new class extends Migration {
8
8
  /**
9
9
  * Run the migrations.
10
10
  *
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4
4
  use Illuminate\Database\Schema\Blueprint;
5
5
  use Illuminate\Support\Facades\Schema;
6
6
 
7
- return new class() extends Migration {
7
+ return new class extends Migration {
8
8
  /**
9
9
  * Run the migrations.
10
10
  *
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4
4
  use Illuminate\Database\Schema\Blueprint;
5
5
  use Illuminate\Support\Facades\Schema;
6
6
 
7
- return new class() extends Migration {
7
+ return new class extends Migration {
8
8
  /**
9
9
  * Run the migrations.
10
10
  *
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4
4
  use Illuminate\Database\Schema\Blueprint;
5
5
  use Illuminate\Support\Facades\Schema;
6
6
 
7
- return new class() extends Migration {
7
+ return new class extends Migration {
8
8
  /**
9
9
  * Run the migrations.
10
10
  *
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4
4
  use Illuminate\Database\Schema\Blueprint;
5
5
  use Illuminate\Support\Facades\Schema;
6
6
 
7
- return new class() extends Migration {
7
+ return new class extends Migration {
8
8
  /**
9
9
  * Run the migrations.
10
10
  *