@fleetbase/storefront-engine 0.3.9 → 0.3.10
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.
- package/addon/components/add-product-as-entity-button.hbs +3 -0
- package/addon/components/add-product-as-entity-button.js +58 -0
- package/addon/components/customer-panel.js +1 -0
- package/addon/components/modals/select-product.hbs +87 -0
- package/addon/components/modals/select-product.js +88 -0
- package/addon/components/order-panel.js +1 -3
- package/addon/components/widget/orders.hbs +2 -2
- package/addon/components/widget/orders.js +1 -0
- package/addon/controllers/products/index/category/new.js +4 -1
- package/addon/engine.js +2 -0
- package/app/components/add-product-as-entity-button.js +1 -0
- package/app/components/modals/select-product.js +1 -0
- package/composer.json +3 -3
- package/extension.json +1 -1
- package/package.json +2 -2
- package/server/src/Http/Controllers/ProductController.php +31 -0
- package/server/src/Models/Product.php +70 -0
- package/server/src/Observers/OrderObserver.php +17 -0
- package/server/src/routes.php +1 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
import { isArray } from '@ember/array';
|
|
5
|
+
import { inject as service } from '@ember/service';
|
|
6
|
+
|
|
7
|
+
export default class AddProductAsEntityButtonComponent extends Component {
|
|
8
|
+
@service modalsManager;
|
|
9
|
+
@service fetch;
|
|
10
|
+
@service notifications;
|
|
11
|
+
@tracked order;
|
|
12
|
+
@tracked controller;
|
|
13
|
+
|
|
14
|
+
constructor(owner, { order, controller }) {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.order = order;
|
|
17
|
+
this.controller = controller;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@action promptProductSelection() {
|
|
21
|
+
this.modalsManager.show('modals/select-product', {
|
|
22
|
+
title: 'Select Product to Add to Order',
|
|
23
|
+
modalClass: 'modal-lg',
|
|
24
|
+
acceptButtonText: 'Add Products',
|
|
25
|
+
acceptButtonDisabled: true,
|
|
26
|
+
selectedProducts: [],
|
|
27
|
+
selectedStorefront: null,
|
|
28
|
+
confirm: (modal) => {
|
|
29
|
+
modal.startLoading();
|
|
30
|
+
|
|
31
|
+
const selectedStorefront = modal.getOption('selectedStorefront');
|
|
32
|
+
const selectedProducts = modal.getOption('selectedProducts', []);
|
|
33
|
+
const products = selectedProducts.map((product) => product.id);
|
|
34
|
+
|
|
35
|
+
return this.fetch
|
|
36
|
+
.post('products/create-entities', { products }, { namespace: 'storefront/int/v1', normalizeToEmberData: true, normalizeModelType: 'entity' })
|
|
37
|
+
.then((entities) => {
|
|
38
|
+
this.controller.addEntities(entities);
|
|
39
|
+
if (isArray(this.order.meta)) {
|
|
40
|
+
this.order.meta.pushObjects([
|
|
41
|
+
{
|
|
42
|
+
key: 'storefront',
|
|
43
|
+
value: selectedStorefront.name
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: 'storefront_id',
|
|
47
|
+
value: selectedStorefront.public_id
|
|
48
|
+
}
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
.catch((error) => {
|
|
53
|
+
this.notifications.serverError(error);
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -7,6 +7,7 @@ import CustomerPanelDetailsComponent from './customer-panel/details';
|
|
|
7
7
|
import CustomerPanelOrdersComponent from './customer-panel/orders';
|
|
8
8
|
import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback';
|
|
9
9
|
import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments';
|
|
10
|
+
|
|
10
11
|
export default class CustomerPanelComponent extends Component {
|
|
11
12
|
/**
|
|
12
13
|
* Service for fetching data.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
|
|
2
|
+
<div class="modal-body-container">
|
|
3
|
+
<div class="grid grid-cols-3 gap-2">
|
|
4
|
+
<InputGroup @name="Select Store">
|
|
5
|
+
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
|
6
|
+
<PowerSelect
|
|
7
|
+
@options={{this.stores}}
|
|
8
|
+
@selected={{this.selectedStorefront}}
|
|
9
|
+
@onChange={{this.onStorefrontSelect}}
|
|
10
|
+
@registerAPI={{this.setStorefrontSelectApi}}
|
|
11
|
+
@optionValue="name"
|
|
12
|
+
@placeholder="Select Storefront"
|
|
13
|
+
@triggerClass="form-select form-input"
|
|
14
|
+
@disabled={{not this.stores}}
|
|
15
|
+
as |storefront|
|
|
16
|
+
>
|
|
17
|
+
{{storefront.name}}
|
|
18
|
+
</PowerSelect>
|
|
19
|
+
</div>
|
|
20
|
+
</InputGroup>
|
|
21
|
+
|
|
22
|
+
{{#if (and this.selectedStorefront this.productCategories)}}
|
|
23
|
+
<InputGroup @name="Select Product Category">
|
|
24
|
+
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
|
25
|
+
<PowerSelect
|
|
26
|
+
@options={{this.productCategories}}
|
|
27
|
+
@selected={{this.selectedCategory}}
|
|
28
|
+
@onChange={{this.onSelectProductCategory}}
|
|
29
|
+
@disabled={{not this.productCategories}}
|
|
30
|
+
@placeholder="Filter by Product Category"
|
|
31
|
+
@triggerClass="form-select form-input"
|
|
32
|
+
as |category|
|
|
33
|
+
>
|
|
34
|
+
{{category.name}}
|
|
35
|
+
</PowerSelect>
|
|
36
|
+
</div>
|
|
37
|
+
</InputGroup>
|
|
38
|
+
{{/if}}
|
|
39
|
+
|
|
40
|
+
{{#if this.selectedProducts}}
|
|
41
|
+
<div class="py-2 flex items-center dark:text-white text-gray-900">
|
|
42
|
+
Selected
|
|
43
|
+
{{pluralize this.selectedProducts.length "Product"}}
|
|
44
|
+
</div>
|
|
45
|
+
{{/if}}
|
|
46
|
+
</div>
|
|
47
|
+
<div class="min-h-4r">
|
|
48
|
+
{{#if this.selectedStorefront}}
|
|
49
|
+
{{#if this.fetchProductsForStorefront.isRunning}}
|
|
50
|
+
<Spinner @loadingMessage="Loading products..." />
|
|
51
|
+
{{else}}
|
|
52
|
+
<div class="grid grid-cols-3 lg:grid-cols-4 gap-2">
|
|
53
|
+
{{#each this.products as |product|}}
|
|
54
|
+
{{#let (in-array product this.selectedProducts) as |isSelected|}}
|
|
55
|
+
<div
|
|
56
|
+
class="border bg-white dark:bg-gray-900 dark:text-gray-100 text-center rounded-md px-2 py-3
|
|
57
|
+
{{if isSelected 'border-blue-500 dark:border-blue-500 outline-offset-2 outline-blue-400 outline-dashed' 'border-gray-200 dark:border-gray-700'}}"
|
|
58
|
+
>
|
|
59
|
+
<div class="flex flex-col items-center justify-center overflow-hidden">
|
|
60
|
+
<div class="mb-3 flex items-center justify-center">
|
|
61
|
+
<img src={{product.primary_image_url}} alt={{product.name}} class="w-24 h-24" />
|
|
62
|
+
</div>
|
|
63
|
+
<h4 class="font-semibold mb-1">{{product.name}}</h4>
|
|
64
|
+
<p class="text-sm truncate">{{product.description}}</p>
|
|
65
|
+
<p class="mb-2 text-sm text-green-400">{{format-currency product.price product.currency}}</p>
|
|
66
|
+
<div class="flex items-center justify-evenly space-x-4">
|
|
67
|
+
<Button
|
|
68
|
+
@type={{if isSelected "danger" "default"}}
|
|
69
|
+
@icon="circle-plus"
|
|
70
|
+
@text={{if isSelected "Remove Product" "Add Product"}}
|
|
71
|
+
@onClick={{fn this.toggleProductSelection product}}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
{{/let}}
|
|
77
|
+
{{else}}
|
|
78
|
+
<div>
|
|
79
|
+
<h3 class="dark:text-gray-100 text-opacity-75 text-sm">No products</h3>
|
|
80
|
+
</div>
|
|
81
|
+
{{/each}}
|
|
82
|
+
</div>
|
|
83
|
+
{{/if}}
|
|
84
|
+
{{/if}}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</Modal::Default>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
import { inject as service } from '@ember/service';
|
|
5
|
+
import { task } from 'ember-concurrency';
|
|
6
|
+
import { pluralize } from 'ember-inflector';
|
|
7
|
+
|
|
8
|
+
export default class ModalsSelectProductComponent extends Component {
|
|
9
|
+
@service fetch;
|
|
10
|
+
@service notifications;
|
|
11
|
+
@service modalsManager;
|
|
12
|
+
@tracked stores = [];
|
|
13
|
+
@tracked productCategories = [];
|
|
14
|
+
@tracked products = [];
|
|
15
|
+
@tracked selectedProducts = [];
|
|
16
|
+
@tracked selectedStorefront;
|
|
17
|
+
@tracked selectedCategory;
|
|
18
|
+
@tracked storefrontSelectAPI;
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
super(...arguments);
|
|
22
|
+
this.fetchStorefronts.perform();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@action toggleProductSelection(product) {
|
|
26
|
+
if (this.selectedProducts.includes(product)) {
|
|
27
|
+
this.selectedProducts.removeObject(product);
|
|
28
|
+
} else {
|
|
29
|
+
this.selectedProducts.pushObject(product);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.updateSelectedProducts();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
updateSelectedProducts() {
|
|
36
|
+
this.modalsManager.setOption('selectedProducts', this.selectedProducts);
|
|
37
|
+
this.modalsManager.setOption('acceptButtonDisabled', this.selectedProducts.length === 0);
|
|
38
|
+
this.modalsManager.setOption('acceptButtonText', this.selectedProducts.length ? `Add ${pluralize(this.selectedProducts.length, 'Product')}` : 'Add Products');
|
|
39
|
+
this.modalsManager.setOption('selectedStorefront', this.selectedStorefront);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@action setStorefrontSelectApi(storefrontSelectAPI) {
|
|
43
|
+
this.storefrontSelectAPI = storefrontSelectAPI;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@action onStorefrontSelect(storefront) {
|
|
47
|
+
this.selectedStorefront = storefront;
|
|
48
|
+
this.selectedCategory = null;
|
|
49
|
+
this.selectedProducts = [];
|
|
50
|
+
this.updateSelectedProducts();
|
|
51
|
+
this.fetchCategoriesForStorefront.perform(storefront);
|
|
52
|
+
this.fetchProductsForStorefront.perform(storefront);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@action onSelectProductCategory(category) {
|
|
56
|
+
this.selectedCategory = category;
|
|
57
|
+
this.fetchProductsForStorefront.perform(this.selectedStorefront, { category_slug: category.slug });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@task *fetchStorefronts(queryParams = {}) {
|
|
61
|
+
try {
|
|
62
|
+
this.stores = yield this.fetch.get('stores', queryParams, { namespace: 'storefront/int/v1', normalizeToEmberData: true });
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.notifications.serverError(error);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this.stores && this.storefrontSelectAPI) {
|
|
69
|
+
this.storefrontSelectAPI.actions.select(this.stores[0]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@task *fetchProductsForStorefront(storefront, queryParams = {}) {
|
|
74
|
+
try {
|
|
75
|
+
this.products = yield this.fetch.get('products', { store_uuid: storefront.id, ...queryParams }, { namespace: 'storefront/int/v1', normalizeToEmberData: true });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
this.notifications.serverError(error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@task *fetchCategoriesForStorefront(storefront, queryParams = {}) {
|
|
82
|
+
try {
|
|
83
|
+
this.productCategories = yield this.fetch.get('categories', { for: 'storefront_product', owner_uuid: storefront.id, limit: -1, ...queryParams }, { normalizeToEmberData: true });
|
|
84
|
+
} catch (error) {
|
|
85
|
+
this.notifications.serverError(error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
|
-
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
3
|
import { action } from '@ember/object';
|
|
4
4
|
import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-context-component-arguments';
|
|
5
5
|
import contextComponentCallback from '@fleetbase/ember-core/utils/context-component-callback';
|
|
6
|
-
import { tracked } from '@glimmer/tracking';
|
|
7
6
|
|
|
8
7
|
export default class OrderPanelComponent extends Component {
|
|
9
8
|
@tracked context = null;
|
|
10
9
|
|
|
11
10
|
constructor() {
|
|
12
11
|
super(...arguments);
|
|
13
|
-
|
|
14
12
|
applyContextComponentArguments(this);
|
|
15
13
|
}
|
|
16
14
|
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
<tr class="h-12">
|
|
32
32
|
<td><a href="javascript:;" {{on "click" (fn this.viewOrder order)}}>{{order.public_id}}</a></td>
|
|
33
33
|
<td>{{format-currency order.meta.total order.meta.currency}}</td>
|
|
34
|
-
<td>{{n-a order.
|
|
35
|
-
<td>{{n-a order.
|
|
34
|
+
<td>{{n-a order.customer.name}}</td>
|
|
35
|
+
<td>{{n-a order.driver_assigned.name}}</td>
|
|
36
36
|
<td>{{order.createdAgo}}</td>
|
|
37
37
|
<td>
|
|
38
38
|
<Badge @status={{order.status}} />
|
|
@@ -4,6 +4,7 @@ import { inject as service } from '@ember/service';
|
|
|
4
4
|
import { inject as controller } from '@ember/controller';
|
|
5
5
|
import { action, computed, get } from '@ember/object';
|
|
6
6
|
import { later } from '@ember/runloop';
|
|
7
|
+
|
|
7
8
|
export default class WidgetOrdersComponent extends Component {
|
|
8
9
|
@service store;
|
|
9
10
|
@service storefront;
|
|
@@ -66,7 +66,10 @@ export default class ProductsIndexCategoryNewController extends BaseController {
|
|
|
66
66
|
|
|
67
67
|
this.loader.removeLoader(loader);
|
|
68
68
|
this.notifications.success(this.intl.t('storefront.products.index.new.new-product-created-success'));
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
yield this.transitionToRoute('products.index.category', category.slug);
|
|
72
|
+
} catch (error) {}
|
|
70
73
|
this.reset();
|
|
71
74
|
}
|
|
72
75
|
|
package/addon/engine.js
CHANGED
|
@@ -5,6 +5,7 @@ import config from './config/environment';
|
|
|
5
5
|
import services from '@fleetbase/ember-core/exports/services';
|
|
6
6
|
import StorefrontKeyMetricsWidget from './components/widget/storefront-key-metrics';
|
|
7
7
|
import StorefrontOrderSummaryComponent from './components/storefront-order-summary';
|
|
8
|
+
import AddProductAsEntityButtonComponent from './components/add-product-as-entity-button';
|
|
8
9
|
|
|
9
10
|
const { modulePrefix } = config;
|
|
10
11
|
const externalRoutes = ['console', 'extensions'];
|
|
@@ -35,6 +36,7 @@ export default class StorefrontEngine extends Engine {
|
|
|
35
36
|
|
|
36
37
|
// register component to views
|
|
37
38
|
universe.registerRenderableComponent('@fleetbase/fleetops-engine', 'fleet-ops:template:operations:orders:view', StorefrontOrderSummaryComponent);
|
|
39
|
+
universe.registerRenderableComponent('@fleetbase/fleetops-engine', 'fleet-ops:template:operations:orders:new:entities-input', AddProductAsEntityButtonComponent);
|
|
38
40
|
|
|
39
41
|
// register widgets
|
|
40
42
|
universe.registerDefaultDashboardWidgets([KeyMetricsWidgetDefinition]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/storefront-engine/components/add-product-as-entity-button';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/storefront-engine/components/modals/select-product';
|
package/composer.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fleetbase/storefront-api",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Headless Commerce & Marketplace Extension for Fleetbase",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fleetbase-extension",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
],
|
|
23
23
|
"require": {
|
|
24
24
|
"php": "^8.0",
|
|
25
|
-
"fleetbase/core-api": "^1.4.
|
|
26
|
-
"fleetbase/fleetops-api": "^0.
|
|
25
|
+
"fleetbase/core-api": "^1.4.26",
|
|
26
|
+
"fleetbase/fleetops-api": "^0.5.0",
|
|
27
27
|
"geocoder-php/google-maps-places-provider": "^1.4",
|
|
28
28
|
"laravel-notification-channels/apn": "^5.0",
|
|
29
29
|
"laravel-notification-channels/fcm": "^4.1",
|
package/extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/storefront-engine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Headless Commerce & Marketplace Extension for Fleetbase",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "storefront",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@fleetbase/ember-core": "^0.2.11",
|
|
47
|
-
"@fleetbase/ember-ui": "^0.2.
|
|
47
|
+
"@fleetbase/ember-ui": "^0.2.17",
|
|
48
48
|
"@fleetbase/fleetops-data": "^0.1.15",
|
|
49
49
|
"@babel/core": "^7.23.2",
|
|
50
50
|
"@fortawesome/ember-fontawesome": "^0.4.1",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\Storefront\Http\Controllers;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Http\Resources\v1\Entity as EntityResource;
|
|
5
6
|
use Fleetbase\FleetOps\Support\Utils;
|
|
6
7
|
use Fleetbase\Models\Category;
|
|
7
8
|
use Fleetbase\Models\File;
|
|
@@ -165,4 +166,34 @@ class ProductController extends StorefrontController
|
|
|
165
166
|
|
|
166
167
|
return response()->json($products);
|
|
167
168
|
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Retrieves a list of product IDs from the request, finds the corresponding Product models, converts each to an Entity, and returns them as a JSON response.
|
|
172
|
+
*
|
|
173
|
+
* This function handles a request that includes an array of product UUIDs. It fetches the corresponding Product models from the database,
|
|
174
|
+
* converts each Product to an Entity using the Product model's toEntity method, and collects these entities. The function finally returns
|
|
175
|
+
* these entities as a JSON response, which can be useful for front-end applications or other services that need structured product data.
|
|
176
|
+
*
|
|
177
|
+
* @param Request $request the request object, expected to contain an array of product UUIDs under the 'products' key
|
|
178
|
+
*
|
|
179
|
+
* @return \Illuminate\Http\JsonResponse Returns a JSON response that contains an array of Entity objects, each representing a product.
|
|
180
|
+
* Each Entity object includes all relevant product details formatted and structured as specified in the Product model's toEntity method.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* // Example usage:
|
|
184
|
+
* POST /storefront/int/v1/products/create-entities
|
|
185
|
+
* Body: { "products": ["uuid1", "uuid2"] }
|
|
186
|
+
*/
|
|
187
|
+
public function createEntities(Request $request)
|
|
188
|
+
{
|
|
189
|
+
$productIds = $request->array('products');
|
|
190
|
+
$products = Product::whereIn('uuid', $productIds)->get();
|
|
191
|
+
$entities = [];
|
|
192
|
+
|
|
193
|
+
foreach ($products as $product) {
|
|
194
|
+
$entities[] = $product->createAsEntity();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return EntityResource::collection($entities);
|
|
198
|
+
}
|
|
168
199
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\Storefront\Models;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\Casts\Json;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Entity;
|
|
6
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
7
8
|
use Fleetbase\Models\Category;
|
|
8
9
|
use Fleetbase\Models\File;
|
|
@@ -430,4 +431,73 @@ class Product extends StorefrontModel
|
|
|
430
431
|
|
|
431
432
|
return $results;
|
|
432
433
|
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Converts a Product model instance to an Entity object, which represents a more detailed and structured form of the product data.
|
|
437
|
+
*
|
|
438
|
+
* This function utilizes the properties of the Product model along with any additional attributes provided to construct a new Entity object.
|
|
439
|
+
* The Entity object contains detailed information about the product, including identifiers, names, pricing information, and meta attributes.
|
|
440
|
+
* Meta attributes can be dynamically added to extend the data structure with additional custom information.
|
|
441
|
+
*
|
|
442
|
+
* @param array $additionalAttributes Optional. Additional attributes that can be merged into the product's meta information.
|
|
443
|
+
* This array can include any custom data under the 'meta' key, which is merged with the default meta attributes.
|
|
444
|
+
* Default is an empty array.
|
|
445
|
+
*
|
|
446
|
+
* @return Entity Returns a new Entity object populated with product information and any additional meta attributes.
|
|
447
|
+
* The Entity object is structured with a set of predefined keys (e.g., company_uuid, photo_uuid) and can be customized with additional meta attributes.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* // Example usage:
|
|
451
|
+
* $product = new Product();
|
|
452
|
+
* $entity = $product->toEntity(['meta' => ['custom_attribute' => 'value']]);
|
|
453
|
+
*/
|
|
454
|
+
public function toEntity(array $additionalAttributes = []): Entity
|
|
455
|
+
{
|
|
456
|
+
$meta = data_get($additionalAttributes, 'meta', []);
|
|
457
|
+
|
|
458
|
+
return new Entity([
|
|
459
|
+
'company_uuid' => session('company'),
|
|
460
|
+
'photo_uuid' => $this->primary_image_uuid,
|
|
461
|
+
'internal_id' => $this->public_id,
|
|
462
|
+
'name' => $this->name,
|
|
463
|
+
'description' => $this->description,
|
|
464
|
+
'currency' => $this->currency,
|
|
465
|
+
'sku' => $this->sku,
|
|
466
|
+
'price' => $this->price,
|
|
467
|
+
'sale_price' => $this->sale_price,
|
|
468
|
+
'type' => 'storefront-product',
|
|
469
|
+
...$additionalAttributes,
|
|
470
|
+
'meta' => [
|
|
471
|
+
'product_id' => $this->public_id,
|
|
472
|
+
'image_url' => $this->primary_image_url,
|
|
473
|
+
...$meta,
|
|
474
|
+
],
|
|
475
|
+
]);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Creates a new Entity from the Product model instance and saves it to the database.
|
|
480
|
+
*
|
|
481
|
+
* This function first converts the Product model to an Entity object using the toEntity method. It then saves this Entity object to the
|
|
482
|
+
* database, ensuring that all product details are persisted. This is particularly useful when the Product model needs to be
|
|
483
|
+
* represented and stored as an Entity for operations that require a more complex data structure or additional business logic.
|
|
484
|
+
*
|
|
485
|
+
* @param array $additionalAttributes Optional. Additional attributes that can be passed to the toEntity method to include custom
|
|
486
|
+
* meta information in the Entity creation process. Default is an empty array.
|
|
487
|
+
*
|
|
488
|
+
* @return Entity Returns the Entity object after saving it to the database. This object contains all the product information along
|
|
489
|
+
* with any additional attributes that were passed.
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* // Example usage:
|
|
493
|
+
* $product = new Product();
|
|
494
|
+
* $entity = $product->createAsEntity(['meta' => ['custom_attribute' => 'value']]);
|
|
495
|
+
*/
|
|
496
|
+
public function createAsEntity(array $additionalAttributes = []): Entity
|
|
497
|
+
{
|
|
498
|
+
$entity = $this->toEntity();
|
|
499
|
+
$entity->save();
|
|
500
|
+
|
|
501
|
+
return $entity;
|
|
502
|
+
}
|
|
433
503
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\Storefront\Observers;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
6
|
+
|
|
7
|
+
class OrderObserver
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Handle the Order "created" event.
|
|
11
|
+
*
|
|
12
|
+
* @return void
|
|
13
|
+
*/
|
|
14
|
+
public function created(Order $order)
|
|
15
|
+
{
|
|
16
|
+
}
|
|
17
|
+
}
|
package/server/src/routes.php
CHANGED
|
@@ -145,6 +145,7 @@ Route::prefix(config('storefront.api.routing.prefix', 'storefront'))->namespace(
|
|
|
145
145
|
'products',
|
|
146
146
|
function ($router, $controller) {
|
|
147
147
|
$router->post('process-imports', $controller('processImports'));
|
|
148
|
+
$router->post('create-entities', $controller('createEntities'));
|
|
148
149
|
}
|
|
149
150
|
);
|
|
150
151
|
$router->fleetbaseRoutes('product-hours');
|