@fleetbase/storefront-engine 0.2.4 → 0.2.5

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 (38) 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 +9 -2
  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/routes/customers/index.js +11 -1
  16. package/addon/routes/networks/index.js +9 -0
  17. package/addon/routes/orders/index.js +11 -1
  18. package/addon/routes/products/index.js +8 -2
  19. package/addon/services/storefront.js +81 -0
  20. package/addon/templates/networks/index/network/stores.hbs +2 -2
  21. package/addon/templates/networks/index/network.hbs +1 -1
  22. package/addon/templates/products/index/category/new.hbs +1 -1
  23. package/addon/templates/products/index/category.hbs +20 -3
  24. package/app/controllers/base-controller.js +1 -0
  25. package/assets/sounds/storefront_order_alert.mp3 +0 -0
  26. package/composer.json +1 -1
  27. package/config/environment.js +11 -1
  28. package/index.js +15 -0
  29. package/package.json +132 -125
  30. package/server/src/Expansions/ContactFilterExpansion.php +36 -0
  31. package/server/src/Expansions/OrderFilterExpansion.php +31 -0
  32. package/server/src/Expansions/VendorFilterExpansion.php +36 -0
  33. package/server/src/Http/Resources/Cart.php +9 -1
  34. package/server/src/Http/Resources/Network.php +4 -0
  35. package/server/src/Http/Resources/Product.php +5 -0
  36. package/server/src/Http/Resources/Store.php +4 -0
  37. package/server/src/Http/Resources/StoreLocation.php +1 -2
  38. 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;
@@ -89,6 +90,12 @@ export default class ProductsIndexCategoryNewController extends Controller {
89
90
  }
90
91
 
91
92
  @action queueFile(file) {
93
+ // since we have dropzone and upload button within dropzone validate the file state first
94
+ // as this method can be called twice from both functions
95
+ if (['queued', 'failed', 'timed_out', 'aborted'].indexOf(file.state) === -1) {
96
+ return;
97
+ }
98
+
92
99
  this.uploadQueue.pushObject(file);
93
100
  this.fetch.uploadFile.perform(
94
101
  file,
@@ -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,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,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>
@@ -1,4 +1,4 @@
1
- <Overlay @position="right" @isResizable={{true}} @noBackdrop={{true}} @width="800px" @minResizeWidth={{700}} @fullHeight={{true}}>
1
+ <Overlay @position="right" @isResizable={{true}} @noBackdrop={{true}} @width="570px" @minResizeWidth={{570}} @fullHeight={{true}}>
2
2
  <Overlay::Header @title="Manage Network" @onPressCancel={{this.transitionBack}} />
3
3
  <Overlay::Body class="p-0i" @increaseInnerBodyHeightBy="0" @wrapperClass="space-y-4 pt-4">
4
4
  <div class="flex flex-col">
@@ -1,4 +1,4 @@
1
- <Overlay @position="right" @noBackdrop={{true}} @width="700px" @fullHeight={{true}} @containerClass="border-l border-transparent dark:border-gray-700">
1
+ <Overlay @position="right" @noBackdrop={{true}} @isResizable={{true}} @width="570px" @fullHeight={{true}} @containerClass="border-l border-transparent dark:border-gray-700">
2
2
  <Overlay::Header @title={{this.overlayTitle}} @titleClass="truncate" @titleWrapperClass="w-3/4" @headerLeftClass="w-70pc" @headerLeftInnerClass="w-full flex-1" @onPressCancel={{this.transitionBack}}>
3
3
  <Button @icon={{this.overlayActionButtonIcon}} @type="primary" @text={{this.overlayActionButtonTitle}} @onClick={{this.saveProduct}} @isLoading={{this.isSaving}} @isSubscriptionRequired={{true}} />
4
4
  </Overlay::Header>
@@ -1,6 +1,23 @@
1
- <Layout::Section::Header @title={{concat this.category.name " Products"}} @searchQuery={{this.query}} @onSearch={{perform this.search}}>
2
- <Button @type="default" @text="Edit this category" @icon="edit" @onClick={{fn this.editCategory this.category}} @disabled={{not this.category}} @wrapperClass="mr-2" />
3
- <Button @type="danger" @text="Delete this category" @icon="trash" @onClick={{this.deleteCategory}} @disabled={{not this.category}} />
1
+ <Layout::Section::Header @title={{concat this.category.name " Products"}} @titleClass="truncate" @searchQuery={{this.query}} @onSearch={{perform this.search}}>
2
+ {{!-- <Button @type="default" @text="Edit this category" @textClass="truncate" @icon="edit" @onClick={{fn this.editCategory this.category}} @disabled={{not this.category}} @wrapperClass="mr-2" />
3
+ <Button @type="danger" @text="Delete this category" @textClass="truncate" @icon="trash" @onClick={{this.deleteCategory}} @disabled={{not this.category}} /> --}}
4
+
5
+ {{#if this.category}}
6
+ <DropdownButton @icon="gear" @text="Category" @type="default" @size="sm" @contentClass="dropdown-menu" as |dd|>
7
+ <div class="next-dd-menu">
8
+ <div class="px-1">
9
+ <a href="#" class="next-dd-item" {{on "click" (dropdown-fn dd this.editCategory this.category)}}>
10
+ Edit this category...
11
+ </a>
12
+ </div>
13
+ <div class="px-1">
14
+ <a href="#" class="text-red-500 next-dd-item" {{on "click" (dropdown-fn dd this.deleteCategory)}}>
15
+ Delete this category...
16
+ </a>
17
+ </div>
18
+ </div>
19
+ </DropdownButton>
20
+ {{/if}}
4
21
  </Layout::Section::Header>
5
22
 
6
23
  <div class="h-screen overflow-y-scroll p-6">
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/storefront-engine/controllers/base-controller';
package/composer.json CHANGED
@@ -23,7 +23,7 @@
23
23
  "require": {
24
24
  "php": "^7.4|^8.0",
25
25
  "fleetbase/core-api": "^1.3.2",
26
- "fleetbase/fleetops-api": "^0.3.2",
26
+ "fleetbase/fleetops-api": "^0.3.5",
27
27
  "geocoder-php/google-maps-places-provider": "^1.4",
28
28
  "laravel-notification-channels/apn": "^3.8",
29
29
  "laravel-notification-channels/fcm": "^2.7",
@@ -1,11 +1,12 @@
1
1
  /* eslint-env node */
2
2
  'use strict';
3
- const { name } = require('../package');
3
+ const { name, fleetbase } = require('../package');
4
4
 
5
5
  module.exports = function (environment) {
6
6
  let ENV = {
7
7
  modulePrefix: name,
8
8
  environment,
9
+ mountedEngineRoutePrefix: getMountedEngineRoutePrefix(),
9
10
 
10
11
  defaultValues: {
11
12
  categoryImage: getenv('DEFAULT_CATEGORY_IMAGE', 'https://flb-assets.s3.ap-southeast-1.amazonaws.com/images/fallback-placeholder-1.png'),
@@ -16,6 +17,15 @@ module.exports = function (environment) {
16
17
  return ENV;
17
18
  };
18
19
 
20
+ function getMountedEngineRoutePrefix() {
21
+ let mountedEngineRoutePrefix = 'storefront';
22
+ if (fleetbase && typeof fleetbase.route === 'string') {
23
+ mountedEngineRoutePrefix = fleetbase.route;
24
+ }
25
+
26
+ return `console.${mountedEngineRoutePrefix}.`;
27
+ }
28
+
19
29
  function getenv(variable, defaultValue = null) {
20
30
  return process.env[variable] !== undefined ? process.env[variable] : defaultValue;
21
31
  }
package/index.js CHANGED
@@ -2,6 +2,8 @@
2
2
  const { buildEngine } = require('ember-engines/lib/engine-addon');
3
3
  const { name } = require('./package');
4
4
  const Funnel = require('broccoli-funnel');
5
+ const MergeTrees = require('broccoli-merge-trees');
6
+ const path = require('path');
5
7
 
6
8
  module.exports = buildEngine({
7
9
  name,
@@ -23,4 +25,17 @@ module.exports = buildEngine({
23
25
  isDevelopingAddon() {
24
26
  return true;
25
27
  },
28
+
29
+ treeForPublic: function () {
30
+ const publicTree = this._super.treeForPublic.apply(this, arguments);
31
+
32
+ const addonTree = [
33
+ new Funnel(path.join(__dirname, 'assets'), {
34
+ destDir: '/',
35
+ }),
36
+ ];
37
+
38
+ // Merge the addon tree with the existing tree
39
+ return publicTree ? new MergeTrees([publicTree, ...addonTree], { overwrite: true }) : new MergeTrees([...addonTree], { overwrite: true });
40
+ },
26
41
  });
package/package.json CHANGED
@@ -1,127 +1,134 @@
1
1
  {
2
- "name": "@fleetbase/storefront-engine",
3
- "version": "0.2.4",
4
- "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
- "fleetbase": {
6
- "route": "storefront",
7
- "dashboard": "storefront/int/v1/dashboard"
8
- },
9
- "keywords": [
10
- "fleetbase-extension",
11
- "fleetbase-storefront",
12
- "fleetbase",
13
- "e-commerce",
14
- "headless-commerce",
15
- "headless-marketplace",
16
- "ember-engine",
17
- "ember-addon"
18
- ],
19
- "repository": "https://github.com/fleetbase/storefront",
20
- "license": "MIT",
21
- "author": "Fleetbase Pte Ltd <hello@fleetbase.io>",
22
- "directories": {
23
- "app": "app",
24
- "addon": "addon",
25
- "tests": "tests"
26
- },
27
- "scripts": {
28
- "build": "ember build --environment=production",
29
- "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"",
30
- "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix",
31
- "lint:hbs": "ember-template-lint .",
32
- "lint:hbs:fix": "ember-template-lint . --fix",
33
- "lint:js": "eslint . --cache",
34
- "lint:js:fix": "eslint . --fix",
35
- "start": "ember serve",
36
- "test": "npm-run-all lint test:*",
37
- "test:ember": "ember test",
38
- "test:ember-compatibility": "ember try:each",
39
- "publish:npm": "npm config set registry https://registry.npmjs.org/ && npm publish",
40
- "publish:github": "npm config set '@fleetbase:registry' https://npm.pkg.github.com/ && npm publish"
41
- },
42
- "dependencies": {
43
- "@fleetbase/ember-core": "^0.1.7",
44
- "@fleetbase/ember-ui": "^0.2.4",
45
- "@fleetbase/fleetops-data": "^0.1.3",
46
- "@fortawesome/ember-fontawesome": "^0.4.1",
47
- "@fortawesome/fontawesome-svg-core": "^6.4.0",
48
- "@fortawesome/free-solid-svg-icons": "^6.4.0",
49
- "ember-cli-babel": "^7.26.11",
50
- "ember-cli-es6-transform": "^1.0.0",
51
- "ember-cli-htmlbars": "^6.1.0",
52
- "ember-intl": "^6.0.0-beta.6",
53
- "ember-tag-input": "^3.1.0",
54
- "ember-wormhole": "^0.6.0"
55
- },
56
- "devDependencies": {
57
- "@ember/optional-features": "^2.0.0",
58
- "@ember/test-helpers": "^2.8.1",
59
- "@embroider/test-setup": "^1.8.3",
60
- "@glimmer/component": "^1.1.2",
61
- "@glimmer/tracking": "^1.1.2",
62
- "babel-eslint": "^10.1.0",
63
- "broccoli-asset-rev": "^3.0.0",
64
- "broccoli-funnel": "^3.0.8",
65
- "ember-auto-import": "^2.4.2",
66
- "ember-classic-decorator": "^3.0.0",
67
- "ember-cli": "~4.6.0",
68
- "ember-cli-dependency-checker": "^3.3.1",
69
- "ember-cli-inject-live-reload": "^2.1.0",
70
- "ember-cli-sri": "^2.1.1",
71
- "ember-cli-terser": "^4.0.2",
72
- "ember-concurrency": "^2.3.7",
73
- "ember-concurrency-decorators": "^2.0.3",
74
- "ember-data": "~4.6.0",
75
- "ember-disable-prototype-extensions": "^1.1.3",
76
- "ember-engines": "^0.8.23",
77
- "ember-load-initializers": "^2.1.2",
78
- "ember-math-helpers": "^2.18.2",
79
- "ember-page-title": "^7.0.0",
80
- "ember-qunit": "^5.1.5",
81
- "ember-resolver": "^8.0.3",
82
- "ember-responsive": "^5.0.0",
83
- "ember-source": "~4.6.0",
84
- "ember-source-channel-url": "^3.0.0",
85
- "ember-template-lint": "^4.10.1",
86
- "ember-try": "^2.0.0",
87
- "eslint": "^7.32.0",
88
- "eslint-config-prettier": "^8.5.0",
89
- "eslint-plugin-ember": "^11.0.2",
90
- "eslint-plugin-node": "^11.1.0",
91
- "eslint-plugin-prettier": "^4.2.1",
92
- "eslint-plugin-qunit": "^7.3.1",
93
- "loader.js": "^4.7.0",
94
- "npm-run-all": "^4.1.5",
95
- "prettier": "^2.7.1",
96
- "qunit": "^2.19.1",
97
- "qunit-dom": "^2.0.0",
98
- "webpack": "^5.74.0"
99
- },
100
- "peerDependencies": {
101
- "ember-engines": "^0.8.23"
102
- },
103
- "engines": {
104
- "node": "14.* || >= 16"
105
- },
106
- "ember": {
107
- "edition": "octane"
108
- },
109
- "ember-addon": {
110
- "configPath": "tests/dummy/config"
111
- },
112
- "prettier": {
113
- "trailingComma": "es5",
114
- "tabWidth": 4,
115
- "semi": true,
116
- "singleQuote": true,
117
- "printWidth": 190,
118
- "overrides": [
119
- {
120
- "files": "*.hbs",
121
- "options": {
122
- "singleQuote": false
123
- }
124
- }
125
- ]
126
- }
2
+ "name": "@fleetbase/storefront-engine",
3
+ "version": "0.2.5",
4
+ "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
+ "fleetbase": {
6
+ "route": "storefront",
7
+ "dashboard": "storefront/int/v1/dashboard"
8
+ },
9
+ "keywords": [
10
+ "fleetbase-extension",
11
+ "fleetbase-storefront",
12
+ "fleetbase",
13
+ "e-commerce",
14
+ "headless-commerce",
15
+ "headless-marketplace",
16
+ "ember-engine",
17
+ "ember-addon"
18
+ ],
19
+ "repository": "https://github.com/fleetbase/storefront",
20
+ "license": "MIT",
21
+ "author": "Fleetbase Pte Ltd <hello@fleetbase.io>",
22
+ "directories": {
23
+ "app": "app",
24
+ "addon": "addon",
25
+ "tests": "tests"
26
+ },
27
+ "scripts": {
28
+ "build": "ember build --environment=production",
29
+ "lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
30
+ "lint:css": "stylelint \"**/*.css\"",
31
+ "lint:css:fix": "concurrently \"npm:lint:css -- --fix\"",
32
+ "lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
33
+ "lint:hbs": "ember-template-lint .",
34
+ "lint:hbs:fix": "ember-template-lint . --fix",
35
+ "lint:js": "eslint . --cache",
36
+ "lint:js:fix": "eslint . --fix",
37
+ "start": "ember serve",
38
+ "test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
39
+ "test:ember": "ember test",
40
+ "test:ember-compatibility": "ember try:each",
41
+ "publish:npm": "npm config set registry https://registry.npmjs.org/ && npm publish",
42
+ "publish:github": "npm config set '@fleetbase:registry' https://npm.pkg.github.com/ && npm publish"
43
+ },
44
+ "dependencies": {
45
+ "@fleetbase/ember-core": "^0.1.9",
46
+ "@fleetbase/ember-ui": "^0.2.8",
47
+ "@fleetbase/fleetops-data": "^0.1.6",
48
+ "@babel/core": "^7.23.2",
49
+ "@fortawesome/ember-fontawesome": "^0.4.1",
50
+ "@fortawesome/fontawesome-svg-core": "^6.4.0",
51
+ "@fortawesome/free-solid-svg-icons": "^6.4.0",
52
+ "ember-cli-babel": "^8.2.0",
53
+ "ember-cli-htmlbars": "^6.3.0",
54
+ "ember-intl": "6.3.2",
55
+ "ember-tag-input": "^3.1.0",
56
+ "ember-wormhole": "^0.6.0"
57
+ },
58
+ "devDependencies": {
59
+ "@babel/eslint-parser": "^7.22.15",
60
+ "@babel/plugin-proposal-decorators": "^7.23.2",
61
+ "@ember/optional-features": "^2.0.0",
62
+ "@ember/test-helpers": "^3.2.0",
63
+ "@embroider/test-setup": "^3.0.2",
64
+ "@glimmer/component": "^1.1.2",
65
+ "@glimmer/tracking": "^1.1.2",
66
+ "broccoli-asset-rev": "^3.0.0",
67
+ "broccoli-funnel": "^3.0.8",
68
+ "broccoli-merge-trees": "^4.2.0",
69
+ "concurrently": "^8.2.2",
70
+ "ember-auto-import": "^2.6.3",
71
+ "ember-classic-decorator": "^3.0.0",
72
+ "ember-cli": "~5.4.1",
73
+ "ember-cli-clean-css": "^3.0.0",
74
+ "ember-cli-dependency-checker": "^3.3.2",
75
+ "ember-cli-inject-live-reload": "^2.1.0",
76
+ "ember-cli-sri": "^2.1.1",
77
+ "ember-cli-terser": "^4.0.2",
78
+ "ember-concurrency": "^3.1.1",
79
+ "ember-concurrency-decorators": "^2.0.3",
80
+ "ember-data": "^4.12.5",
81
+ "ember-engines": "^0.9.0",
82
+ "ember-load-initializers": "^2.1.2",
83
+ "ember-math-helpers": "^4.0.0",
84
+ "ember-page-title": "^8.0.0",
85
+ "ember-qunit": "^8.0.1",
86
+ "ember-resolver": "^11.0.1",
87
+ "ember-responsive": "^5.0.0",
88
+ "ember-source": "~5.4.0",
89
+ "ember-source-channel-url": "^3.0.0",
90
+ "ember-template-lint": "^5.11.2",
91
+ "ember-try": "^3.0.0",
92
+ "eslint": "^8.52.0",
93
+ "eslint-config-prettier": "^9.0.0",
94
+ "eslint-plugin-ember": "^11.11.1",
95
+ "eslint-plugin-n": "^16.2.0",
96
+ "eslint-plugin-prettier": "^5.0.1",
97
+ "eslint-plugin-qunit": "^8.0.1",
98
+ "loader.js": "^4.7.0",
99
+ "prettier": "^3.0.3",
100
+ "qunit": "^2.20.0",
101
+ "qunit-dom": "^2.0.0",
102
+ "stylelint": "^15.11.0",
103
+ "stylelint-config-standard": "^34.0.0",
104
+ "stylelint-prettier": "^4.0.2",
105
+ "webpack": "^5.89.0"
106
+ },
107
+ "peerDependencies": {
108
+ "ember-engines": "^0.9.0"
109
+ },
110
+ "engines": {
111
+ "node": ">= 18"
112
+ },
113
+ "ember": {
114
+ "edition": "octane"
115
+ },
116
+ "ember-addon": {
117
+ "configPath": "tests/dummy/config"
118
+ },
119
+ "prettier": {
120
+ "trailingComma": "es5",
121
+ "tabWidth": 4,
122
+ "semi": true,
123
+ "singleQuote": true,
124
+ "printWidth": 190,
125
+ "overrides": [
126
+ {
127
+ "files": "*.hbs",
128
+ "options": {
129
+ "singleQuote": false
130
+ }
131
+ }
132
+ ]
133
+ }
127
134
  }
@@ -0,0 +1,36 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Expansions;
4
+
5
+ use Fleetbase\Build\Expansion;
6
+
7
+ class ContactFilterExpansion implements Expansion
8
+ {
9
+ /**
10
+ * Get the target class to expand.
11
+ *
12
+ * @return string|Class
13
+ */
14
+ public static function target()
15
+ {
16
+ return \Fleetbase\FleetOps\Http\Filter\ContactFilter::class;
17
+ }
18
+
19
+ /**
20
+ * Filter contact's by their storefront order.
21
+ *
22
+ * @return \Closure
23
+ */
24
+ public function storefront()
25
+ {
26
+ return function (?string $storefront) {
27
+ /** @var \Fleetbase\FleetOps\Http\Filter\ContactFilter $this */
28
+ $this->builder->whereHas(
29
+ 'customerOrders',
30
+ function ($query) use ($storefront) {
31
+ $query->where('meta->storefront_id', $storefront);
32
+ }
33
+ );
34
+ };
35
+ }
36
+ }
@@ -0,0 +1,31 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Expansions;
4
+
5
+ use Fleetbase\Build\Expansion;
6
+
7
+ class OrderFilterExpansion implements Expansion
8
+ {
9
+ /**
10
+ * Get the target class to expand.
11
+ *
12
+ * @return string|Class
13
+ */
14
+ public static function target()
15
+ {
16
+ return \Fleetbase\FleetOps\Http\Filter\OrderFilter::class;
17
+ }
18
+
19
+ /**
20
+ * Filter orders by the storefront id.
21
+ *
22
+ * @return \Closure
23
+ */
24
+ public function storefront()
25
+ {
26
+ return function (?string $storefront) {
27
+ /** @var \Fleetbase\FleetOps\Http\Filter\OrderFilter $this */
28
+ $this->builder->where('meta->storefront_id', $storefront);
29
+ };
30
+ }
31
+ }
@@ -0,0 +1,36 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Expansions;
4
+
5
+ use Fleetbase\Build\Expansion;
6
+
7
+ class VendorFilterExpansion implements Expansion
8
+ {
9
+ /**
10
+ * Get the target class to expand.
11
+ *
12
+ * @return string|Class
13
+ */
14
+ public static function target()
15
+ {
16
+ return \Fleetbase\FleetOps\Http\Filter\VendorFilter::class;
17
+ }
18
+
19
+ /**
20
+ * Filter vendor's by their storefront order.
21
+ *
22
+ * @return \Closure
23
+ */
24
+ public function storefront()
25
+ {
26
+ return function (?string $storefront) {
27
+ /** @var \Fleetbase\FleetOps\Http\Filter\VendorFilter $this */
28
+ $this->builder->whereHas(
29
+ 'customerOrders',
30
+ function ($query) use ($storefront) {
31
+ $query->where('meta->storefront_id', $storefront);
32
+ }
33
+ );
34
+ };
35
+ }
36
+ }
@@ -3,6 +3,7 @@
3
3
  namespace Fleetbase\Storefront\Http\Resources;
4
4
 
5
5
  use Fleetbase\Http\Resources\FleetbaseResource;
6
+ use Fleetbase\Support\Http;
6
7
 
7
8
  class Cart extends FleetbaseResource
8
9
  {
@@ -15,7 +16,14 @@ class Cart extends FleetbaseResource
15
16
  public function toArray($request)
16
17
  {
17
18
  return [
18
- 'id' => $this->public_id,
19
+ 'id' => $this->when(Http::isInternalRequest(), $this->id,$this->public_id),
20
+ 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
21
+ 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
22
+ 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
23
+ 'user_uuid' => $this->when(Http::isInternalRequest(), $this->user_uuid),
24
+ 'checkout_uuid' => $this->when(Http::isInternalRequest(), $this->checkout_uuid),
25
+ 'checkout_uuid' => $this->when(Http::isInternalRequest(), $this->checkout_uuid),
26
+ 'customer_id' => $this->customer_id,
19
27
  'currency' => $this->currency,
20
28
  'subtotal' => $this->subtotal,
21
29
  'total_items' => $this->total_items,
@@ -20,6 +20,10 @@ class Network extends FleetbaseResource
20
20
  'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
21
21
  'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
22
22
  'key' => $this->when(Http::isInternalRequest(), $this->key),
23
+ 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
24
+ 'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
25
+ 'logo_uuid' => $this->when(Http::isInternalRequest(), $this->logo_uuid),
26
+ 'backdrop_uuid' => $this->when(Http::isInternalRequest(), $this->backdrop_uuid),
23
27
  'name' => $this->name,
24
28
  'description' => $this->description,
25
29
  'translations' => $this->translations ?? [],
@@ -21,6 +21,11 @@ class Product extends FleetbaseResource
21
21
  'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
22
22
  'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
23
23
  'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
24
+ 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
25
+ 'store_uuid' => $this->when(Http::isInternalRequest(), $this->store_uuid),
26
+ 'category_uuid' => $this->when(Http::isInternalRequest(), $this->category_uuid),
27
+ 'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
28
+ 'primary_image_uuid' => $this->when(Http::isInternalRequest(), $this->primary_image_uuid),
24
29
  'name' => $this->name,
25
30
  'description' => $this->description,
26
31
  'sku' => $this->sku,
@@ -20,6 +20,10 @@ class Store extends FleetbaseResource
20
20
  'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
21
21
  'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
22
22
  'key' => $this->when(Http::isInternalRequest(), $this->key),
23
+ 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
24
+ 'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
25
+ 'logo_uuid' => $this->when(Http::isInternalRequest(), $this->logo_uuid),
26
+ 'backdrop_uuid' => $this->when(Http::isInternalRequest(), $this->backdrop_uuid),
23
27
  'name' => $this->name,
24
28
  'description' => $this->description,
25
29
  'translations' => $this->translations ?? [],
@@ -3,7 +3,6 @@
3
3
  namespace Fleetbase\Storefront\Http\Resources;
4
4
 
5
5
  use Fleetbase\FleetOps\Http\Resources\v1\Place;
6
- use Fleetbase\FleetOps\Http\Resources\Internal\v1\Place as InternalPlace;
7
6
  use Fleetbase\Http\Resources\FleetbaseResource;
8
7
  use Fleetbase\Support\Http;
9
8
 
@@ -24,7 +23,7 @@ class StoreLocation extends FleetbaseResource
24
23
  'store' => data_get($this, 'store.public_id'),
25
24
  'store_data' => $this->when($request->boolean('with_store'), new Store($this->store)),
26
25
  'name' => $this->name,
27
- 'place' => $this->when(Http::isInternalRequest(), new InternalPlace($this->place), new Place($this->place)),
26
+ 'place' => $this->place ? new Place($this->place) : null,
28
27
  'hours' => StoreHour::collection($this->hours),
29
28
  'created_at' => $this->created_at,
30
29
  'updated_at' => $this->updated_at,
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "declarationDir": "declarations",
5
+ "emitDeclarationOnly": true,
6
+ "noEmit": false,
7
+ "rootDir": "."
8
+ },
9
+ "include": ["addon", "addon-test-support"]
10
+ }