@fleetbase/storefront-engine 0.2.4 → 0.2.6

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 (140) hide show
  1. package/README.md +4 -4
  2. package/addon/components/modals/import-products.hbs +1 -1
  3. package/addon/components/schedule-manager.hbs +2 -2
  4. package/addon/components/widget/customers.hbs +1 -1
  5. package/addon/components/widget/customers.js +7 -4
  6. package/addon/components/widget/orders.js +4 -0
  7. package/addon/controllers/base-controller.js +31 -0
  8. package/addon/controllers/networks/index/network.js +2 -2
  9. package/addon/controllers/networks/index.js +3 -2
  10. package/addon/controllers/products/index/category/new.js +14 -11
  11. package/addon/controllers/products/index/category.js +2 -2
  12. package/addon/controllers/products/index/index.js +2 -2
  13. package/addon/controllers/products/index.js +2 -3
  14. package/addon/models/network.js +0 -1
  15. package/addon/models/product-addon-category.js +10 -1
  16. package/addon/models/product-variant.js +0 -2
  17. package/addon/models/product.js +35 -2
  18. package/addon/routes/customers/index.js +11 -1
  19. package/addon/routes/networks/index.js +9 -0
  20. package/addon/routes/orders/index.js +11 -1
  21. package/addon/routes/products/index.js +8 -2
  22. package/addon/serializers/product.js +16 -0
  23. package/addon/services/storefront.js +81 -0
  24. package/addon/templates/networks/index/network/stores.hbs +2 -2
  25. package/addon/templates/networks/index/network.hbs +1 -1
  26. package/addon/templates/products/index/category/new.hbs +3 -3
  27. package/addon/templates/products/index/category.hbs +20 -3
  28. package/app/controllers/base-controller.js +1 -0
  29. package/assets/sounds/storefront_order_alert.mp3 +0 -0
  30. package/composer.json +3 -3
  31. package/config/environment.js +11 -1
  32. package/extension.json +1 -1
  33. package/index.js +15 -0
  34. package/package.json +132 -125
  35. package/server/src/Auth/Schemas/Storefront.php +31 -39
  36. package/server/src/Console/Commands/NotifyStorefrontOrderNearby.php +9 -11
  37. package/server/src/Expansions/ContactFilterExpansion.php +36 -0
  38. package/server/src/Expansions/EntityExpansion.php +12 -12
  39. package/server/src/Expansions/OrderFilterExpansion.php +31 -0
  40. package/server/src/Expansions/VendorFilterExpansion.php +36 -0
  41. package/server/src/Http/Controllers/ActionController.php +9 -11
  42. package/server/src/Http/Controllers/AddonCategoryController.php +1 -1
  43. package/server/src/Http/Controllers/CustomerController.php +1 -1
  44. package/server/src/Http/Controllers/GatewayController.php +1 -1
  45. package/server/src/Http/Controllers/MetricsController.php +17 -17
  46. package/server/src/Http/Controllers/NetworkController.php +16 -26
  47. package/server/src/Http/Controllers/NotificationChannelController.php +1 -1
  48. package/server/src/Http/Controllers/OrderController.php +18 -22
  49. package/server/src/Http/Controllers/ProductAddonCategoryController.php +1 -2
  50. package/server/src/Http/Controllers/ProductAddonController.php +1 -2
  51. package/server/src/Http/Controllers/ProductController.php +89 -44
  52. package/server/src/Http/Controllers/ProductHourController.php +1 -1
  53. package/server/src/Http/Controllers/ProductVariantController.php +1 -1
  54. package/server/src/Http/Controllers/ProductVariantOptionController.php +1 -1
  55. package/server/src/Http/Controllers/ReviewController.php +1 -1
  56. package/server/src/Http/Controllers/StoreController.php +1 -2
  57. package/server/src/Http/Controllers/StoreHourController.php +1 -1
  58. package/server/src/Http/Controllers/StoreLocationController.php +1 -1
  59. package/server/src/Http/Controllers/StorefrontController.php +0 -2
  60. package/server/src/Http/Controllers/VoteController.php +1 -1
  61. package/server/src/Http/Controllers/v1/CartController.php +16 -26
  62. package/server/src/Http/Controllers/v1/CategoryController.php +3 -4
  63. package/server/src/Http/Controllers/v1/CheckoutController.php +283 -286
  64. package/server/src/Http/Controllers/v1/CustomerController.php +54 -67
  65. package/server/src/Http/Controllers/v1/GatewayControllerController.php +0 -2
  66. package/server/src/Http/Controllers/v1/NetworkController.php +26 -23
  67. package/server/src/Http/Controllers/v1/PaymentMethodController.php +0 -2
  68. package/server/src/Http/Controllers/v1/ProductController.php +1 -4
  69. package/server/src/Http/Controllers/v1/ReviewController.php +31 -35
  70. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +57 -53
  71. package/server/src/Http/Controllers/v1/StoreController.php +8 -16
  72. package/server/src/Http/Filter/AddonCategoryFilter.php +1 -1
  73. package/server/src/Http/Filter/OrderFilter.php +2 -2
  74. package/server/src/Http/Middleware/SetStorefrontSession.php +9 -20
  75. package/server/src/Http/Requests/AddStoreToNetworkCategory.php +3 -3
  76. package/server/src/Http/Requests/CreateCustomerRequest.php +5 -7
  77. package/server/src/Http/Requests/CreateReviewRequest.php +4 -5
  78. package/server/src/Http/Requests/GetServiceQuoteFromCart.php +2 -2
  79. package/server/src/Http/Requests/InitializeCheckoutRequest.php +5 -5
  80. package/server/src/Http/Requests/NetworkActionRequest.php +2 -2
  81. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +2 -2
  82. package/server/src/Http/Resources/Cart.php +20 -11
  83. package/server/src/Http/Resources/Category.php +12 -11
  84. package/server/src/Http/Resources/Customer.php +15 -14
  85. package/server/src/Http/Resources/Gateway.php +12 -11
  86. package/server/src/Http/Resources/Media.php +9 -8
  87. package/server/src/Http/Resources/Network.php +33 -28
  88. package/server/src/Http/Resources/Product.php +89 -81
  89. package/server/src/Http/Resources/Review.php +14 -13
  90. package/server/src/Http/Resources/ReviewCustomer.php +12 -11
  91. package/server/src/Http/Resources/Store.php +39 -33
  92. package/server/src/Http/Resources/StoreHour.php +9 -8
  93. package/server/src/Http/Resources/StoreLocation.php +9 -9
  94. package/server/src/Imports/ProductsImport.php +0 -2
  95. package/server/src/Jobs/DownloadProductImageUrl.php +7 -4
  96. package/server/src/Listeners/HandleOrderCompleted.php +2 -1
  97. package/server/src/Listeners/HandleOrderDispatched.php +2 -1
  98. package/server/src/Listeners/HandleOrderDriverAssigned.php +2 -1
  99. package/server/src/Listeners/HandleOrderStarted.php +4 -3
  100. package/server/src/Mail/StorefrontNetworkInvite.php +7 -6
  101. package/server/src/Models/AddonCategory.php +2 -4
  102. package/server/src/Models/Cart.php +117 -124
  103. package/server/src/Models/Checkout.php +14 -12
  104. package/server/src/Models/Customer.php +9 -9
  105. package/server/src/Models/Gateway.php +22 -22
  106. package/server/src/Models/Network.php +41 -52
  107. package/server/src/Models/NetworkStore.php +6 -5
  108. package/server/src/Models/NotificationChannel.php +11 -9
  109. package/server/src/Models/PaymentMethod.php +9 -7
  110. package/server/src/Models/Product.php +152 -30
  111. package/server/src/Models/ProductAddon.php +14 -11
  112. package/server/src/Models/ProductAddonCategory.php +9 -8
  113. package/server/src/Models/ProductHour.php +6 -5
  114. package/server/src/Models/ProductStoreLocation.php +7 -5
  115. package/server/src/Models/ProductVariant.php +15 -11
  116. package/server/src/Models/ProductVariantOption.php +12 -9
  117. package/server/src/Models/Review.php +9 -7
  118. package/server/src/Models/Store.php +68 -84
  119. package/server/src/Models/StoreHour.php +6 -5
  120. package/server/src/Models/StoreLocation.php +12 -8
  121. package/server/src/Models/StorefrontModel.php +1 -1
  122. package/server/src/Models/Vote.php +8 -6
  123. package/server/src/Notifications/StorefrontOrderCanceled.php +15 -19
  124. package/server/src/Notifications/StorefrontOrderCompleted.php +15 -19
  125. package/server/src/Notifications/StorefrontOrderCreated.php +31 -36
  126. package/server/src/Notifications/StorefrontOrderDriverAssigned.php +15 -20
  127. package/server/src/Notifications/StorefrontOrderEnroute.php +14 -19
  128. package/server/src/Notifications/StorefrontOrderNearby.php +16 -21
  129. package/server/src/Notifications/StorefrontOrderPreparing.php +15 -18
  130. package/server/src/Notifications/StorefrontOrderReadyForPickup.php +15 -18
  131. package/server/src/Observers/NetworkObserver.php +2 -3
  132. package/server/src/Observers/ProductObserver.php +17 -33
  133. package/server/src/Providers/EventServiceProvider.php +15 -4
  134. package/server/src/Providers/StorefrontServiceProvider.php +8 -10
  135. package/server/src/Support/Metrics.php +12 -12
  136. package/server/src/Support/OrderConfig.php +3 -2
  137. package/server/src/Support/QPay.php +22 -20
  138. package/server/src/Support/Storefront.php +18 -17
  139. package/server/src/routes.php +1 -1
  140. package/tsconfig.declarations.json +10 -0
package/README.md CHANGED
@@ -15,10 +15,10 @@ This monorepo contains both the frontend and backend components of the Storefron
15
15
 
16
16
  ### Requirements
17
17
 
18
- - PHP 7.3.0 or above
19
- - Ember.js v3.24 or above
20
- - Ember CLI v3.24 or above
21
- - Node.js v14 or above
18
+ * PHP 7.3.0 or above
19
+ * Ember.js v4.8 or above
20
+ * Ember CLI v4.8 or above
21
+ * Node.js v18 or above
22
22
 
23
23
  ## Structure
24
24
 
@@ -3,7 +3,7 @@
3
3
  {{#if @options.isProcessing}}
4
4
  <div class="min-h-56 dropzone w-full rounded px-4 py-8 min-h text-gray-900 dark:text-white text-center flex flex-col items-center justify-center border-2 border-dashed border-gray-200 dark:border-indigo-500">
5
5
  <div class="flex items-center justify-center py-5">
6
- <PageLoader class="text-sm dar:text-gray-100" @loadingMessage="Processing import..." />
6
+ <Spinner class="text-sm dark:text-gray-100" @loadingMessage="Processing import..." />
7
7
  </div>
8
8
  </div>
9
9
  {{else}}
@@ -2,9 +2,9 @@
2
2
  {{#each-in this.schedule as |day hours|}}
3
3
  <div class="content-panel {{@contentPanelClass}} shadow-sm">
4
4
  <div class="content-panel-header items-center {{@contentPanelHeaderClass}}">
5
- <h4 class="font-semibold dark:text-gray-100">{{day}}</h4>
5
+ <h4 class="font-semibold text-sm dark:text-gray-100">{{day}}</h4>
6
6
  <div>
7
- <Button @icon="calendar-week" @text="Add Hours" @onClick={{fn this.addHours @subject day}} class="{{@addHoursButtonClass}}" />
7
+ <Button @icon="calendar-week" @text="Add Hours" @textClass="truncate text-xs" @size="sm" @onClick={{fn this.addHours @subject day}} class="{{@addHoursButtonClass}}" />
8
8
  </div>
9
9
  </div>
10
10
  <div class="content-panel-body {{@contentPanelBodyClass}}">
@@ -1,4 +1,4 @@
1
- <ContentPanel @title={{this.title}} @titleStatusRight={{this.customers.length}} @titleStatuRightClass="info-status-badge" @hideStatusDot={{true}} @open={{this.customers.length}} @pad={{false}} @wrapperClass={{@wrapperClass}} @onInsert={{this.getCustomers}}>
1
+ <ContentPanel @title={{this.title}} @titleStatusRight={{this.customers.length}} @titleStatusRightClass="info-status-badge" @hideStatusDot={{true}} @open={{this.customers.length}} @pad={{false}} @wrapperClass={{@wrapperClass}} @onInsert={{this.getCustomers}}>
2
2
  {{#if this.isLoading}}
3
3
  <div class="px-3 py-2">
4
4
  <Spinner class="text-sky-400" />
@@ -1,16 +1,19 @@
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 { action } from '@ember/object';
5
+ import setComponentArg from '@fleetbase/ember-core/utils/set-component-arg';
5
6
 
6
7
  export default class WidgetCustomersComponent extends Component {
7
8
  @service store;
8
9
  @service storefront;
9
10
  @tracked isLoading = true;
10
11
  @tracked customers = [];
12
+ @tracked title = 'Recent Customers';
11
13
 
12
- @computed('args.title') get title() {
13
- return this.args.title || 'Recent Customers';
14
+ constructor(owner, { title }) {
15
+ super(...arguments);
16
+ setComponentArg(this, 'title', title);
14
17
  }
15
18
 
16
19
  @action async getCustomers() {
@@ -28,7 +31,7 @@ export default class WidgetCustomersComponent extends Component {
28
31
  this.isLoading = true;
29
32
 
30
33
  return new Promise((resolve) => {
31
- const storefront = this.storefront?.activeStore?.public_id;
34
+ const storefront = this.storefront.getActiveStore('public_id');
32
35
 
33
36
  if (!storefront) {
34
37
  this.isLoading = false;
@@ -17,6 +17,10 @@ export default class WidgetOrdersComponent extends Component {
17
17
  return this.args.title ?? 'Recent Orders';
18
18
  }
19
19
 
20
+ constructor() {
21
+ super(...arguments);
22
+ }
23
+
20
24
  @action async setupWidget() {
21
25
  later(
22
26
  this,
@@ -0,0 +1,31 @@
1
+ import Controller from '@ember/controller';
2
+ import { inject as service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+
5
+ export default class BaseController extends Controller {
6
+ /**
7
+ * Inject the `universe` service
8
+ *
9
+ * @var {Service}
10
+ */
11
+ @service universe;
12
+
13
+ /**
14
+ * Transitions to a specified route within the '@fleetbase/storefront-engine' engine.
15
+ *
16
+ * This action is a wrapper around the `transitionToEngineRoute` method of the `universe` service (or object),
17
+ * specifically targeting the '@fleetbase/storefront-engine'. It allows for easy transitioning to routes
18
+ * within this engine, abstracting away the need to repeatedly specify the engine name.
19
+ *
20
+ * @param {string} route - The route within the '@fleetbase/storefront-engine' to transition to.
21
+ * @param {...any} args - Additional arguments to pass to the transitionToEngineRoute method.
22
+ * @returns {Promise} A Promise that resolves with the result of the transitionToEngineRoute method.
23
+ *
24
+ * @example
25
+ * // To transition to the 'management.fleets.index.new' route within the '@fleetbase/storefront-engine'
26
+ * this.transitionToRoute('management.fleets.index.new');
27
+ */
28
+ @action transitionToRoute(route, ...args) {
29
+ return this.universe.transitionToEngineRoute('@fleetbase/storefront-engine', route, ...args);
30
+ }
31
+ }
@@ -1,8 +1,8 @@
1
- import Controller from '@ember/controller';
1
+ import BaseController from '@fleetbase/storefront-engine/controllers/base-controller';
2
2
  import { inject as service } from '@ember/service';
3
3
  import { action } from '@ember/object';
4
4
 
5
- export default class NetworksIndexNetworkController extends Controller {
5
+ export default class NetworksIndexNetworkController extends BaseController {
6
6
  @service modalsManager;
7
7
 
8
8
  @action transitionBack({ closeOverlay }) {
@@ -1,4 +1,5 @@
1
- import Controller, { inject as controller } from '@ember/controller';
1
+ import BaseController from '@fleetbase/storefront-engine/controllers/base-controller';
2
+ import { inject as controller } from '@ember/controller';
2
3
  import { inject as service } from '@ember/service';
3
4
  import { tracked } from '@glimmer/tracking';
4
5
  import { action } from '@ember/object';
@@ -6,7 +7,7 @@ import { isBlank } from '@ember/utils';
6
7
  import { timeout } from 'ember-concurrency';
7
8
  import { task } from 'ember-concurrency-decorators';
8
9
 
9
- export default class NetworksIndexController extends Controller {
10
+ export default class NetworksIndexController extends BaseController {
10
11
  /**
11
12
  * Inject the `NetworksIndexNetworkStoresController` controller
12
13
  *
@@ -1,4 +1,5 @@
1
- import Controller, { inject as controller } from '@ember/controller';
1
+ import BaseController from '@fleetbase/storefront-engine/controllers/base-controller';
2
+ import { inject as controller } from '@ember/controller';
2
3
  import { tracked } from '@glimmer/tracking';
3
4
  import { isArray } from '@ember/array';
4
5
  import { action } from '@ember/object';
@@ -6,7 +7,7 @@ import { alias } from '@ember/object/computed';
6
7
  import { underscore } from '@ember/string';
7
8
  import { inject as service } from '@ember/service';
8
9
 
9
- export default class ProductsIndexCategoryNewController extends Controller {
10
+ export default class ProductsIndexCategoryNewController extends BaseController {
10
11
  @controller('products.index.category') productsIndexCategoryController;
11
12
  @service notifications;
12
13
  @service modalsManager;
@@ -16,6 +17,7 @@ export default class ProductsIndexCategoryNewController extends Controller {
16
17
  @service fetch;
17
18
  @service loader;
18
19
  @service crud;
20
+ @service hostRouter;
19
21
  @alias('storefront.activeStore') activeStore;
20
22
  @tracked product = this.store.createRecord('product', { store_uuid: this.activeStore.id, currency: this.activeStore.currency, tags: [], meta_array: [] });
21
23
  @tracked uploadQueue = [];
@@ -57,15 +59,13 @@ export default class ProductsIndexCategoryNewController extends Controller {
57
59
  this.product
58
60
  .serializeMeta()
59
61
  .save()
60
- .then((product) => {
62
+ .then(() => {
61
63
  this.loader.removeLoader(loader);
62
64
  this.isSaving = false;
63
65
  this.notifications.success('New product created successfully!');
64
66
 
65
67
  return this.transitionToRoute('products.index.category').then(() => {
66
- console.log(this.productsIndexCategoryController);
67
- console.log(this.productsIndexCategoryController.products);
68
- this.productsIndexCategoryController?.products?.pushObject(product);
68
+ return this.hostRouter.refresh();
69
69
  });
70
70
  })
71
71
  .catch((error) => {
@@ -80,8 +80,7 @@ export default class ProductsIndexCategoryNewController extends Controller {
80
80
  this.product.tags = [];
81
81
  }
82
82
 
83
- this.product.tags?.pushObject(tag);
84
- console.log('this.product.tags', this.product.tags);
83
+ this.product.tags.pushObject(tag);
85
84
  }
86
85
 
87
86
  @action removeTag(index) {
@@ -89,6 +88,12 @@ export default class ProductsIndexCategoryNewController extends Controller {
89
88
  }
90
89
 
91
90
  @action queueFile(file) {
91
+ // since we have dropzone and upload button within dropzone validate the file state first
92
+ // as this method can be called twice from both functions
93
+ if (['queued', 'failed', 'timed_out', 'aborted'].indexOf(file.state) === -1) {
94
+ return;
95
+ }
96
+
92
97
  this.uploadQueue.pushObject(file);
93
98
  this.fetch.uploadFile.perform(
94
99
  file,
@@ -184,7 +189,7 @@ export default class ProductsIndexCategoryNewController extends Controller {
184
189
  });
185
190
  });
186
191
 
187
- product.addon_categories = productAddonCategories;
192
+ product.addon_categories.pushObjects(productAddonCategories);
188
193
  },
189
194
  });
190
195
  });
@@ -258,8 +263,6 @@ export default class ProductsIndexCategoryNewController extends Controller {
258
263
  } else {
259
264
  productAddonCategory.excluded_addons.pushObject(id);
260
265
  }
261
-
262
- console.log(productAddonCategory);
263
266
  }
264
267
 
265
268
  @action addMetaField() {
@@ -1,4 +1,4 @@
1
- import Controller from '@ember/controller';
1
+ import BaseController from '@fleetbase/storefront-engine/controllers/base-controller';
2
2
  import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
4
  import { action } from '@ember/object';
@@ -7,7 +7,7 @@ import { isBlank } from '@ember/utils';
7
7
  import { timeout } from 'ember-concurrency';
8
8
  import { task } from 'ember-concurrency-decorators';
9
9
 
10
- export default class ProductsIndexCategoryController extends Controller {
10
+ export default class ProductsIndexCategoryController extends BaseController {
11
11
  @service modalsManager;
12
12
  @service fetch;
13
13
  @service hostRouter;
@@ -1,4 +1,4 @@
1
- import Controller from '@ember/controller';
1
+ import BaseController from '@fleetbase/storefront-engine/controllers/base-controller';
2
2
  import { inject as service } from '@ember/service';
3
3
  import { tracked } from '@glimmer/tracking';
4
4
  import { action } from '@ember/object';
@@ -6,7 +6,7 @@ import { isBlank } from '@ember/utils';
6
6
  import { timeout } from 'ember-concurrency';
7
7
  import { task } from 'ember-concurrency-decorators';
8
8
 
9
- export default class ProductsIndexIndexController extends Controller {
9
+ export default class ProductsIndexIndexController extends BaseController {
10
10
  /**
11
11
  * Inject the `filters` service
12
12
  *
@@ -1,15 +1,14 @@
1
- import Controller from '@ember/controller';
1
+ import BaseController from '@fleetbase/storefront-engine/controllers/base-controller';
2
2
  import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
4
  import { action } from '@ember/object';
5
5
  import { alias } from '@ember/object/computed';
6
6
  import { dasherize } from '@ember/string';
7
7
 
8
- export default class ProductsIndexController extends Controller {
8
+ export default class ProductsIndexController extends BaseController {
9
9
  @service store;
10
10
  @service modalsManager;
11
11
  @service currentUser;
12
- @service modalsManager;
13
12
  @service notifications;
14
13
  @service fetch;
15
14
  @service hostRouter;
@@ -15,7 +15,6 @@ export default class NetworkModel extends Model {
15
15
  @hasMany('store') stores;
16
16
  @hasMany('notification-channel') notification_channels;
17
17
  @hasMany('gateway') gateways;
18
- @hasMany('store') stores;
19
18
  @belongsTo('file') logo;
20
19
  @belongsTo('file') backdrop;
21
20
 
@@ -1,5 +1,6 @@
1
1
  import Model, { attr, belongsTo } from '@ember-data/model';
2
2
  import { format, formatDistanceToNow } from 'date-fns';
3
+ import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
3
4
 
4
5
  export default class ProductAddonCategoryModel extends Model {
5
6
  /** @ids */
@@ -19,7 +20,15 @@ export default class ProductAddonCategoryModel extends Model {
19
20
 
20
21
  /** @methods */
21
22
  toJSON() {
22
- return this.serialize();
23
+ return {
24
+ uuid: this.id,
25
+ category_uuid: this.category_uuid,
26
+ product_uuid: this.product_uuid,
27
+ name: this.name,
28
+ excluded_addons: getWithDefault(this, 'excluded_addons', []),
29
+ updated_at: this.updated_at,
30
+ created_at: this.created_at,
31
+ };
23
32
  }
24
33
 
25
34
  /** @computed */
@@ -4,8 +4,6 @@ import { format, formatDistanceToNow } from 'date-fns';
4
4
  export default class ProductVariantModel extends Model {
5
5
  /** @ids */
6
6
  @attr('string') product_uuid;
7
- @attr('string') created_by_uuid;
8
- @attr('string') company_uuid;
9
7
 
10
8
  /** @relationships */
11
9
  @hasMany('product-variant-option') options;
@@ -20,7 +20,7 @@ export default class ProductModel extends Model {
20
20
  @belongsTo('file') primary_image;
21
21
  @hasMany('file') files;
22
22
  @hasMany('product-variant', { async: false }) variants;
23
- @hasMany('product-addon-category') addon_categories;
23
+ @hasMany('product-addon-category', { async: false }) addon_categories;
24
24
  @hasMany('product-hour') hours;
25
25
 
26
26
  /** @attributes */
@@ -56,7 +56,40 @@ export default class ProductModel extends Model {
56
56
 
57
57
  /** @methods */
58
58
  toJSON() {
59
- return this.serialize();
59
+ return {
60
+ uuid: this.id,
61
+ created_by_uuid: this.created_by_uuid,
62
+ company_uuid: this.company_uuid,
63
+ store_uuid: this.store_uuid,
64
+ category_uuid: this.category_uuid,
65
+ primary_image_uuid: this.primary_image_uuid,
66
+ public_id: this.public_id,
67
+ name: this.name,
68
+ category: this.category,
69
+ primary_image: this.primary_image,
70
+ files: this.files,
71
+ variants: this.variants,
72
+ addon_categories: this.addon_categories,
73
+ hours: this.hours,
74
+ description: this.description,
75
+ primary_image_url: this.primary_image_url,
76
+ sku: this.sku,
77
+ currency: this.currency,
78
+ price: this.price,
79
+ sale_price: this.sale_price,
80
+ tags: this.tags,
81
+ youtube_urls: this.youtube_urls,
82
+ translations: this.translations,
83
+ is_on_sale: this.is_on_sale,
84
+ is_recommended: this.is_recommended,
85
+ is_service: this.is_service,
86
+ is_available: this.is_available,
87
+ is_bookable: this.is_bookable,
88
+ status: this.status,
89
+ slug: this.slug,
90
+ created_at: this.created_at,
91
+ updated_at: this.updated_at,
92
+ };
60
93
  }
61
94
 
62
95
  /** @computed */
@@ -1,8 +1,11 @@
1
1
  import Route from '@ember/routing/route';
2
2
  import { inject as service } from '@ember/service';
3
+ import { action, set } from '@ember/object';
4
+ import isNestedRouteTransition from '@fleetbase/ember-core/utils/is-nested-route-transition';
3
5
 
4
6
  export default class CustomersIndexRoute extends Route {
5
7
  @service store;
8
+ @service storefront;
6
9
 
7
10
  queryParams = {
8
11
  page: { refreshModel: true },
@@ -19,7 +22,14 @@ export default class CustomersIndexRoute extends Route {
19
22
  updated_at: { refreshModel: true },
20
23
  };
21
24
 
25
+ @action willTransition(transition) {
26
+ if (isNestedRouteTransition(transition)) {
27
+ set(this.queryParams, 'page.refreshModel', false);
28
+ set(this.queryParams, 'sort.refreshModel', false);
29
+ }
30
+ }
31
+
22
32
  model(params) {
23
- return this.store.query('customer', params);
33
+ return this.store.query('customer', { ...params, storefront: this.storefront.getActiveStore('public_id') });
24
34
  }
25
35
  }
@@ -1,5 +1,7 @@
1
1
  import Route from '@ember/routing/route';
2
2
  import { inject as service } from '@ember/service';
3
+ import { action, set } from '@ember/object';
4
+ import isNestedRouteTransition from '@fleetbase/ember-core/utils/is-nested-route-transition';
3
5
 
4
6
  export default class NetworksIndexRoute extends Route {
5
7
  @service store;
@@ -13,6 +15,13 @@ export default class NetworksIndexRoute extends Route {
13
15
  updated_at: { refreshModel: true },
14
16
  };
15
17
 
18
+ @action willTransition(transition) {
19
+ if (isNestedRouteTransition(transition)) {
20
+ set(this.queryParams, 'page.refreshModel', false);
21
+ set(this.queryParams, 'sort.refreshModel', false);
22
+ }
23
+ }
24
+
16
25
  model(params) {
17
26
  return this.store.query('network', params);
18
27
  }
@@ -1,8 +1,11 @@
1
1
  import Route from '@ember/routing/route';
2
2
  import { inject as service } from '@ember/service';
3
+ import { action, set } from '@ember/object';
4
+ import isNestedRouteTransition from '@fleetbase/ember-core/utils/is-nested-route-transition';
3
5
 
4
6
  export default class OrdersIndexRoute extends Route {
5
7
  @service store;
8
+ @service storefront;
6
9
 
7
10
  queryParams = {
8
11
  page: { refreshModel: true },
@@ -23,7 +26,14 @@ export default class OrdersIndexRoute extends Route {
23
26
  before: { refreshModel: true },
24
27
  };
25
28
 
29
+ @action willTransition(transition) {
30
+ if (isNestedRouteTransition(transition)) {
31
+ set(this.queryParams, 'page.refreshModel', false);
32
+ set(this.queryParams, 'sort.refreshModel', false);
33
+ }
34
+ }
35
+
26
36
  model(params) {
27
- return this.store.query('order', params);
37
+ return this.store.query('order', { ...params, storefront: this.storefront.getActiveStore('public_id') });
28
38
  }
29
39
  }
@@ -1,13 +1,19 @@
1
1
  import Route from '@ember/routing/route';
2
2
  import { inject as service } from '@ember/service';
3
- import { action } from '@ember/object';
3
+ import { action, set } from '@ember/object';
4
+ import isNestedRouteTransition from '@fleetbase/ember-core/utils/is-nested-route-transition';
4
5
 
5
6
  export default class ProductsIndexRoute extends Route {
6
7
  @service store;
7
8
  @service currentUser;
8
9
 
9
- @action willTransition() {
10
+ @action willTransition(transition) {
10
11
  this.controller.category = null;
12
+
13
+ if (isNestedRouteTransition(transition)) {
14
+ set(this.queryParams, 'page.refreshModel', false);
15
+ set(this.queryParams, 'sort.refreshModel', false);
16
+ }
11
17
  }
12
18
 
13
19
  model(params = {}) {
@@ -1,5 +1,6 @@
1
1
  import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
2
2
  import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
3
+ import { isArray } from '@ember/array';
3
4
 
4
5
  export default class ProductSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
5
6
  /**
@@ -17,4 +18,19 @@ export default class ProductSerializer extends ApplicationSerializer.extend(Embe
17
18
  category: { embedded: 'always' },
18
19
  };
19
20
  }
21
+
22
+ serializeHasMany(snapshot, json, relationship) {
23
+ let key = relationship.key;
24
+
25
+ if (key === 'addon_categories') {
26
+ const addonCategories = snapshot.record.get('addon_categories');
27
+
28
+ if (isArray(addonCategories)) {
29
+ json.addon_categories = addonCategories;
30
+ }
31
+ return;
32
+ }
33
+
34
+ return super.serializeHasMany(...arguments);
35
+ }
20
36
  }
@@ -1,24 +1,86 @@
1
1
  import Service from '@ember/service';
2
2
  import Evented from '@ember/object/evented';
3
3
  import { inject as service } from '@ember/service';
4
+ import { get } from '@ember/object';
4
5
 
6
+ /**
7
+ * Service to manage storefront operations.
8
+ */
5
9
  export default class StorefrontService extends Service.extend(Evented) {
10
+ /**
11
+ * Ember service for data store.
12
+ * @type {Service}
13
+ */
6
14
  @service store;
15
+
16
+ /**
17
+ * Ember service for fetch operations.
18
+ * @type {Service}
19
+ */
7
20
  @service fetch;
21
+
22
+ /**
23
+ * Ember service for notifications.
24
+ * @type {Service}
25
+ */
8
26
  @service notifications;
27
+
28
+ /**
29
+ * Ember service for current user information.
30
+ * @type {Service}
31
+ */
9
32
  @service currentUser;
33
+
34
+ /**
35
+ * Ember service for managing modals.
36
+ * @type {Service}
37
+ */
10
38
  @service modalsManager;
39
+
40
+ /**
41
+ * Ember service for router operations.
42
+ * @type {Service}
43
+ */
11
44
  @service hostRouter;
12
45
 
46
+ /**
47
+ * Gets the active store.
48
+ * @returns {Object} The active store object.
49
+ */
13
50
  get activeStore() {
14
51
  return this.findActiveStore();
15
52
  }
16
53
 
54
+ /**
55
+ * Sets the active storefront.
56
+ * @param {Object} store - The store to set as active.
57
+ */
17
58
  setActiveStorefront(store) {
18
59
  this.currentUser.setOption('activeStorefront', store.id);
19
60
  this.trigger('storefront.changed', store);
20
61
  }
21
62
 
63
+ /**
64
+ * Gets the active store or a specific property of it.
65
+ * @param {string|null} property - The property to retrieve from the active store.
66
+ * @returns {Object|null} The active store or its specific property.
67
+ */
68
+ getActiveStore(property = null) {
69
+ if (this.activeStore) {
70
+ if (typeof property === 'string') {
71
+ return get(this.activeStore, property);
72
+ }
73
+
74
+ return this.activeStore;
75
+ }
76
+
77
+ return null;
78
+ }
79
+
80
+ /**
81
+ * Finds the active store based on the current user's settings.
82
+ * @returns {Object|null} The active store object or null if not found.
83
+ */
22
84
  findActiveStore() {
23
85
  const activeStoreId = this.currentUser.getOption('activeStorefront');
24
86
 
@@ -43,6 +105,11 @@ export default class StorefrontService extends Service.extend(Evented) {
43
105
  return activeStore;
44
106
  }
45
107
 
108
+ /**
109
+ * Alerts about an incoming order.
110
+ * @param {string} orderId - The ID of the incoming order.
111
+ * @param {Object} store - The store associated with the order.
112
+ */
46
113
  async alertIncomingOrder(orderId, store) {
47
114
  const order = await this.store.queryRecord('order', {
48
115
  public_id: orderId,
@@ -80,6 +147,9 @@ export default class StorefrontService extends Service.extend(Evented) {
80
147
  });
81
148
  }
82
149
 
150
+ /**
151
+ * Listens for incoming orders and handles them.
152
+ */
83
153
  async listenForIncomingOrders() {
84
154
  const store = this.findActiveStore();
85
155
 
@@ -109,6 +179,10 @@ export default class StorefrontService extends Service.extend(Evented) {
109
179
  this.hostRouter.on('routeWillChange', channel.close);
110
180
  }
111
181
 
182
+ /**
183
+ * Creates the first store with given options.
184
+ * @param {Object} [options={}] - Options for creating the first store.
185
+ */
112
186
  createFirstStore(options = {}) {
113
187
  const store = this.store.createRecord('store');
114
188
  const currency = this.currentUser.getWhoisProperty('currency.code');
@@ -151,6 +225,10 @@ export default class StorefrontService extends Service.extend(Evented) {
151
225
  });
152
226
  }
153
227
 
228
+ /**
229
+ * Creates a new storefront with given options.
230
+ * @param {Object} [options={}] - Options for creating the new storefront.
231
+ */
154
232
  createNewStorefront(options = {}) {
155
233
  const store = this.store.createRecord('store');
156
234
  const currency = this.currentUser.getWhoisProperty('currency.code');
@@ -191,6 +269,9 @@ export default class StorefrontService extends Service.extend(Evented) {
191
269
  });
192
270
  }
193
271
 
272
+ /**
273
+ * Plays an alert sound.
274
+ */
194
275
  playAlert() {
195
276
  // eslint-disable-next-line no-undef
196
277
  const alert = new Audio('/sounds/storefront_order_alert.mp3');
@@ -1,6 +1,6 @@
1
1
  <Layout::Section::Header @title="Stores" @searchQuery={{this.storeQuery}} @onSearch={{perform this.search}}>
2
- <Button @type="primary" @icon="plus" @text="Add Stores" @onClick={{this.addStores}} @wrapperClass="mr-2" />
3
- <Button @type="primary" @icon="envelope-open-text" @text="Invite Stores" @onClick={{this.invite}} />
2
+ <Button @type="primary" @icon="plus" @text="Add Stores" @textClass="truncate" @size="sm" @onClick={{this.addStores}} @wrapperClass="mr-2" />
3
+ <Button @type="primary" @icon="envelope-open-text" @textClass="truncate" @size="sm" @text="Invite Stores" @onClick={{this.invite}} />
4
4
  </Layout::Section::Header>
5
5
 
6
6
  <Layout::Section::Body>