@fleetbase/storefront-engine 0.3.23 → 0.3.25

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 (112) hide show
  1. package/addon/adapters/catalog.js +1 -0
  2. package/addon/adapters/food-truck.js +1 -0
  3. package/addon/components/customer-panel/orders.hbs +7 -1
  4. package/addon/components/modals/assign-food-truck-catalogs.hbs +14 -0
  5. package/addon/components/modals/create-catalog.hbs +96 -0
  6. package/addon/components/modals/create-first-store.hbs +6 -1
  7. package/addon/components/modals/create-food-truck.hbs +69 -0
  8. package/addon/components/modals/manage-addons.js +1 -15
  9. package/addon/components/order-panel.hbs +2 -2
  10. package/addon/components/widget/customers.hbs +5 -5
  11. package/addon/components/widget/orders.hbs +7 -4
  12. package/addon/components/widget/orders.js +11 -1
  13. package/addon/controllers/catalogs/index.js +121 -0
  14. package/addon/controllers/food-trucks/index.js +100 -0
  15. package/addon/models/catalog-category.js +6 -0
  16. package/addon/models/catalog-hour.js +72 -0
  17. package/addon/models/catalog.js +45 -0
  18. package/addon/models/food-truck.js +47 -0
  19. package/addon/models/product.js +1 -0
  20. package/addon/routes/catalogs/index.js +22 -0
  21. package/addon/routes/catalogs.js +3 -0
  22. package/addon/routes/food-trucks/index.js +22 -0
  23. package/addon/routes/food-trucks.js +3 -0
  24. package/addon/routes.js +6 -0
  25. package/addon/serializers/catalog-category.js +15 -0
  26. package/addon/serializers/catalog.js +16 -0
  27. package/addon/serializers/food-truck.js +18 -0
  28. package/addon/serializers/product-variant.js +0 -1
  29. package/addon/services/storefront.js +1 -1
  30. package/addon/styles/storefront-engine.css +15 -5
  31. package/addon/templates/application.hbs +14 -0
  32. package/addon/templates/catalogs/index.hbs +46 -0
  33. package/addon/templates/catalogs.hbs +1 -0
  34. package/addon/templates/customers/index.hbs +1 -1
  35. package/addon/templates/food-trucks/index.hbs +52 -0
  36. package/addon/templates/food-trucks.hbs +1 -0
  37. package/addon/templates/networks/index.hbs +14 -2
  38. package/addon/templates/products/index/category/new.hbs +10 -3
  39. package/addon/templates/products/index/index.hbs +1 -2
  40. package/addon/templates/products/index.hbs +16 -2
  41. package/app/adapters/catalog.js +1 -0
  42. package/app/adapters/food-truck.js +1 -0
  43. package/app/components/modals/assign-food-truck-catalogs.js +1 -0
  44. package/app/components/modals/create-catalog.js +1 -0
  45. package/app/components/modals/create-food-truck.js +1 -0
  46. package/app/controllers/catalogs/index.js +1 -0
  47. package/app/controllers/food-trucks/index.js +1 -0
  48. package/app/models/catalog-category.js +1 -0
  49. package/app/models/catalog-hour.js +1 -0
  50. package/app/models/catalog.js +1 -0
  51. package/app/models/food-truck.js +1 -0
  52. package/app/routes/catalogs/index.js +1 -0
  53. package/app/routes/catalogs.js +1 -0
  54. package/app/routes/food-trucks/index.js +1 -0
  55. package/app/routes/food-trucks.js +1 -0
  56. package/app/serializers/catalog-category.js +1 -0
  57. package/app/serializers/catalog.js +1 -0
  58. package/app/serializers/food-truck.js +1 -0
  59. package/app/templates/catalogs/index.js +1 -0
  60. package/app/templates/catalogs.js +1 -0
  61. package/app/templates/food-trucks/index.js +1 -0
  62. package/app/templates/food-trucks.js +1 -0
  63. package/composer.json +1 -1
  64. package/extension.json +1 -1
  65. package/package.json +1 -1
  66. package/server/migrations/2025_01_30_042853_create_catalogs_table.php +41 -0
  67. package/server/migrations/2025_01_30_044728_create_catalog_category_products_table.php +35 -0
  68. package/server/migrations/2025_01_30_050611_create_food_trucks_table.php +72 -0
  69. package/server/migrations/2025_01_30_052157_create_catalog_subjects_table.php +54 -0
  70. package/server/migrations/2025_01_30_052402_create_catalog_hours_table.php +39 -0
  71. package/server/src/Auth/Schemas/Storefront.php +16 -0
  72. package/server/src/Console/Commands/PurgeExpiredCarts.php +49 -0
  73. package/server/src/Http/Controllers/CatalogCategoryController.php +13 -0
  74. package/server/src/Http/Controllers/CatalogController.php +13 -0
  75. package/server/src/Http/Controllers/CatalogHourController.php +13 -0
  76. package/server/src/Http/Controllers/FoodTruckController.php +13 -0
  77. package/server/src/Http/Controllers/v1/CatalogController.php +38 -0
  78. package/server/src/Http/Controllers/v1/CategoryController.php +1 -1
  79. package/server/src/Http/Controllers/v1/FoodTruckController.php +39 -0
  80. package/server/src/Http/Controllers/v1/OrderController.php +43 -0
  81. package/server/src/Http/Controllers/v1/ProductController.php +1 -1
  82. package/server/src/Http/Controllers/v1/ReviewController.php +8 -6
  83. package/server/src/Http/Filter/FoodTruckFilter.php +37 -0
  84. package/server/src/Http/Resources/Catalog.php +34 -0
  85. package/server/src/Http/Resources/CatalogCategory.php +38 -0
  86. package/server/src/Http/Resources/CatalogProduct.php +55 -0
  87. package/server/src/Http/Resources/FoodTruck.php +42 -0
  88. package/server/src/Http/Resources/Product.php +2 -1
  89. package/server/src/Models/AddonCategory.php +12 -0
  90. package/server/src/Models/Cart.php +6 -0
  91. package/server/src/Models/Catalog.php +213 -0
  92. package/server/src/Models/CatalogCategory.php +118 -0
  93. package/server/src/Models/CatalogHour.php +46 -0
  94. package/server/src/Models/CatalogProduct.php +25 -0
  95. package/server/src/Models/CatalogSubject.php +70 -0
  96. package/server/src/Models/FoodTruck.php +182 -0
  97. package/server/src/Models/Product.php +16 -1
  98. package/server/src/Notifications/StorefrontOrderCanceled.php +80 -121
  99. package/server/src/Notifications/StorefrontOrderCompleted.php +86 -131
  100. package/server/src/Notifications/StorefrontOrderCreated.php +29 -15
  101. package/server/src/Notifications/StorefrontOrderDriverAssigned.php +96 -130
  102. package/server/src/Notifications/StorefrontOrderEnroute.php +89 -129
  103. package/server/src/Notifications/StorefrontOrderNearby.php +103 -132
  104. package/server/src/Notifications/StorefrontOrderPreparing.php +89 -134
  105. package/server/src/Notifications/StorefrontOrderReadyForPickup.php +89 -134
  106. package/server/src/Observers/CatalogObserver.php +40 -0
  107. package/server/src/Observers/FoodTruckObserver.php +24 -0
  108. package/server/src/Observers/ProductObserver.php +3 -27
  109. package/server/src/Providers/StorefrontServiceProvider.php +7 -3
  110. package/server/src/Support/PushNotification.php +127 -0
  111. package/server/src/routes.php +15 -0
  112. package/translations/en-us.yaml +5 -0
@@ -0,0 +1,55 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Resources;
4
+
5
+ use Fleetbase\Support\Http;
6
+
7
+ class CatalogProduct extends Product
8
+ {
9
+ /**
10
+ * Transform the resource into an array.
11
+ *
12
+ * @param \Illuminate\Http\Request $request
13
+ *
14
+ * @return array
15
+ */
16
+ public function toArray($request)
17
+ {
18
+ return [
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
+ 'store_uuid' => $this->when(Http::isInternalRequest(), $this->store_uuid),
24
+ 'category_uuid' => $this->when(Http::isInternalRequest(), $this->category_uuid),
25
+ 'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
26
+ 'primary_image_uuid' => $this->when(Http::isInternalRequest(), $this->primary_image_uuid),
27
+ 'name' => $this->name,
28
+ 'description' => $this->description,
29
+ 'sku' => $this->sku,
30
+ 'primary_image_url' => $this->primary_image_url,
31
+ 'price' => $this->price,
32
+ 'sale_price' => $this->sale_price,
33
+ 'currency' => $this->currency,
34
+ 'is_on_sale' => $this->is_on_sale,
35
+ 'is_recommended' => $this->is_recommended,
36
+ 'is_service' => $this->is_service,
37
+ 'is_bookable' => $this->is_bookable,
38
+ 'is_available' => $this->is_available,
39
+ 'tags' => $this->tags ?? [],
40
+ 'status' => $this->status,
41
+ 'slug' => $this->slug,
42
+ 'translations' => $this->translations ?? [],
43
+ 'addon_categories' => $this->mapAddonCategories($this->addonCategories),
44
+ 'variants' => $this->mapVariants($this->variants),
45
+ 'files' => $this->when(Http::isInternalRequest(), $this->files),
46
+ 'images' => $this->when(!Http::isInternalRequest(), $this->mapFiles($this->files)),
47
+ 'videos' => $this->when(!Http::isInternalRequest(), $this->mapFiles($this->files, 'video')),
48
+ 'hours' => $this->mapHours($this->hours),
49
+ 'youtube_urls' => $this->youtube_urls ?? [],
50
+ 'created_at' => $this->created_at,
51
+ 'updated_at' => $this->updated_at,
52
+ 'type' => $this->when(Http::isInternalRequest(), 'product'),
53
+ ];
54
+ }
55
+ }
@@ -0,0 +1,42 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Resources;
4
+
5
+ use Fleetbase\FleetOps\Http\Resources\v1\ServiceArea as ServiceAreaResource;
6
+ use Fleetbase\FleetOps\Http\Resources\v1\Vehicle as VehicleResource;
7
+ use Fleetbase\FleetOps\Http\Resources\v1\Zone as ZoneResource;
8
+ use Fleetbase\Http\Resources\FleetbaseResource;
9
+ use Fleetbase\Support\Http;
10
+
11
+ class FoodTruck extends FleetbaseResource
12
+ {
13
+ /**
14
+ * Transform the resource into an array.
15
+ *
16
+ * @param \Illuminate\Http\Request $request
17
+ *
18
+ * @return array
19
+ */
20
+ public function toArray($request)
21
+ {
22
+ return [
23
+ 'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
24
+ 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
25
+ 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
26
+ 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
27
+ 'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
28
+ 'store_uuid' => $this->when(Http::isInternalRequest(), $this->store_uuid),
29
+ 'service_area_uuid' => $this->when(Http::isInternalRequest(), $this->service_area_uuid),
30
+ 'zone_uuid' => $this->when(Http::isInternalRequest(), $this->zone_uuid),
31
+ 'vehicle_uuid' => $this->when(Http::isInternalRequest(), $this->vehicle_uuid),
32
+ 'vehicle' => $this->vehicle ? new VehicleResource($this->vehicle) : null,
33
+ 'service_area' => $this->serviceArea ? new ServiceAreaResource($this->serviceArea) : null,
34
+ 'zone' => $this->zone ? new ZoneResource($this->zone) : null,
35
+ 'catalogs' => Catalog::collection($this->catalogs ?? []),
36
+ 'online' => $this->vehicle ? $this->vehicle->online : false,
37
+ 'status' => $this->status,
38
+ 'created_at' => $this->created_at,
39
+ 'updated_at' => $this->updated_at,
40
+ ];
41
+ }
42
+ }
@@ -5,8 +5,8 @@ namespace Fleetbase\Storefront\Http\Resources;
5
5
  use Fleetbase\Http\Resources\FleetbaseResource;
6
6
  use Fleetbase\Support\Http;
7
7
  use Illuminate\Support\Arr;
8
- use Illuminate\Support\Str;
9
8
  use Illuminate\Support\Collection;
9
+ use Illuminate\Support\Str;
10
10
 
11
11
  class Product extends FleetbaseResource
12
12
  {
@@ -53,6 +53,7 @@ class Product extends FleetbaseResource
53
53
  'youtube_urls' => $this->youtube_urls ?? [],
54
54
  'created_at' => $this->created_at,
55
55
  'updated_at' => $this->updated_at,
56
+ 'type' => $this->when(Http::isInternalRequest(), 'product'),
56
57
  ];
57
58
  }
58
59
 
@@ -20,6 +20,18 @@ class AddonCategory extends Category
20
20
  */
21
21
  protected string $payloadKey = 'addon_category';
22
22
 
23
+ /**
24
+ * Override the boot method to set "for" automatically.
25
+ */
26
+ protected static function boot()
27
+ {
28
+ parent::boot();
29
+
30
+ static::creating(function (Category $model) {
31
+ $model->for = 'storefront_product_addon';
32
+ });
33
+ }
34
+
23
35
  /**
24
36
  * @return \Illuminate\Database\Eloquent\Relations\HasMany
25
37
  */
@@ -334,6 +334,12 @@ class Cart extends StorefrontModel
334
334
  'addons' => $addons,
335
335
  ];
336
336
 
337
+ // If item was added from a food truck
338
+ if ($storeLocationId && Str::startsWith($storeLocationId, 'food_truck_')) {
339
+ $properties['food_truck_id'] = $storeLocationId;
340
+ $properties['store_location_id'] = Utils::get($product, 'store.locations.0.public_id');
341
+ }
342
+
337
343
  $this->updateCurrency($product->currency, false);
338
344
 
339
345
  foreach ($properties as $prop => $value) {
@@ -0,0 +1,213 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Models;
4
+
5
+ use Fleetbase\Casts\Json;
6
+ use Fleetbase\Models\Company;
7
+ use Fleetbase\Models\User;
8
+ use Fleetbase\Traits\HasApiModelBehavior;
9
+ use Fleetbase\Traits\HasPublicid;
10
+ use Fleetbase\Traits\HasUuid;
11
+ use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
+ use Illuminate\Database\Eloquent\Relations\HasMany;
13
+ use Illuminate\Database\Eloquent\Relations\MorphToMany;
14
+
15
+ class Catalog extends StorefrontModel
16
+ {
17
+ use HasUuid;
18
+ use HasPublicid;
19
+ use HasApiModelBehavior;
20
+
21
+ /**
22
+ * The type of public ID to generate.
23
+ *
24
+ * @var string
25
+ */
26
+ protected $publicIdType = 'catalog';
27
+
28
+ /**
29
+ * The database table used by the model.
30
+ *
31
+ * @var string
32
+ */
33
+ protected $table = 'catalogs';
34
+
35
+ /**
36
+ * Searchable columns.
37
+ *
38
+ * @var array
39
+ */
40
+ protected $searchableColumns = ['name'];
41
+
42
+ /**
43
+ * The attributes that are mass assignable.
44
+ *
45
+ * @var array
46
+ */
47
+ protected $fillable = [
48
+ 'uuid',
49
+ 'store_uuid',
50
+ 'company_uuid',
51
+ 'created_by_uuid',
52
+ 'name',
53
+ 'description',
54
+ 'status',
55
+ 'meta',
56
+ ];
57
+
58
+ /**
59
+ * The attributes that should be cast to native types.
60
+ *
61
+ * @var array
62
+ */
63
+ protected $casts = [
64
+ 'meta' => Json::class,
65
+ ];
66
+
67
+ /**
68
+ * Dynamic attributes that are appended to object.
69
+ *
70
+ * @var array
71
+ */
72
+ protected $appends = [];
73
+
74
+ /**
75
+ * The attributes excluded from the model's JSON form.
76
+ *
77
+ * @var array
78
+ */
79
+ protected $hidden = [];
80
+
81
+ /**
82
+ * Get the user who created the catalog.
83
+ */
84
+ public function createdBy(): BelongsTo
85
+ {
86
+ return $this
87
+ ->setConnection(config('fleetbase.connection.db'))
88
+ ->belongsTo(User::class, 'created_by_uuid', 'uuid');
89
+ }
90
+
91
+ /**
92
+ * Get the company this catalog belongs to.
93
+ */
94
+ public function company(): BelongsTo
95
+ {
96
+ return $this
97
+ ->setConnection(config('fleetbase.connection.db'))
98
+ ->belongsTo(Company::class, 'company_uuid', 'uuid');
99
+ }
100
+
101
+ /**
102
+ * Get the store that owns this food truck.
103
+ */
104
+ public function store(): BelongsTo
105
+ {
106
+ return $this->belongsTo(Store::class, 'store_uuid', 'uuid');
107
+ }
108
+
109
+ /**
110
+ * Hours the catalog is available to customers.
111
+ */
112
+ public function hours(): HasMany
113
+ {
114
+ return $this->hasMany(CatalogHour::class, 'catalog_uuid', 'uuid');
115
+ }
116
+
117
+ /**
118
+ * Categories the catalog contains.
119
+ */
120
+ public function categories(): HasMany
121
+ {
122
+ return $this->setConnection(config('fleetbase.connection.db'))->hasMany(CatalogCategory::class, 'owner_uuid', 'uuid');
123
+ }
124
+
125
+ /**
126
+ * Assignments of subjects the catalog has.
127
+ */
128
+ public function assignments(): HasMany
129
+ {
130
+ return $this->hasMany(CatalogSubject::class, 'catalog_uuid', 'uuid');
131
+ }
132
+
133
+ /**
134
+ * Subjects the catalog has been assigned too.
135
+ */
136
+ public function subjects(): MorphToMany
137
+ {
138
+ return $this->morphedByMany(
139
+ StorefrontModel::class,
140
+ 'subject',
141
+ 'catalog_subjects',
142
+ 'catalog_uuid',
143
+ 'subject_uuid'
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Update or create categories for this catalog based on the provided array.
149
+ *
150
+ * This method:
151
+ * 1) Deletes categories that are no longer in the `$categories` list.
152
+ * 2) Updates existing categories if their UUID is found in `$categories`.
153
+ * 3) Creates new categories for entries without a UUID.
154
+ * 4) Delegates product assignment to `setProducts()` on each category.
155
+ *
156
+ * @param array $categories An array of category data, each containing:
157
+ * - 'uuid' (optional): if provided, indicates an existing category to update
158
+ * - 'name': the category name
159
+ * - 'products' (optional): an array of product UUIDs or objects to link
160
+ *
161
+ * @return $this
162
+ */
163
+ public function setCategories(array $categories = []): Catalog
164
+ {
165
+ // Ensure categories relation is loaded.
166
+ $this->loadMissing('categories');
167
+
168
+ // Grab all existing categories for this catalog.
169
+ $existingCategories = $this->categories;
170
+
171
+ // Extract incoming category UUIDs (if any).
172
+ $incomingUuids = collect($categories)->pluck('uuid')->filter()->unique();
173
+
174
+ // 1) Delete categories that are no longer present.
175
+ // (i.e., they exist in DB but their UUID is not in $incomingUuids)
176
+ $existingCategories
177
+ ->whereNotIn('uuid', $incomingUuids)
178
+ ->each(function (CatalogCategory $cat) {
179
+ $cat->delete(); // or $cat->forceDelete() if you don't want soft deletes
180
+ });
181
+
182
+ // 2) Loop through incoming categories array
183
+ foreach ($categories as $categoryData) {
184
+ $categoryUuid = data_get($categoryData, 'uuid');
185
+ $products = data_get($categoryData, 'products', []);
186
+ $name = data_get($categoryData, 'name');
187
+
188
+ // Try to find an existing category
189
+ $categoryRecord = null;
190
+ if ($categoryUuid) {
191
+ $categoryRecord = $existingCategories->firstWhere('uuid', $categoryUuid);
192
+ }
193
+
194
+ if ($categoryRecord) {
195
+ // 2a) Update existing category
196
+ $categoryRecord->update(['name' => $name]);
197
+ } else {
198
+ // 2b) Create a new category
199
+ $categoryRecord = CatalogCategory::create([
200
+ 'name' => $name,
201
+ 'company_uuid' => $this->company_uuid,
202
+ 'owner_uuid' => $this->uuid,
203
+ 'owner_type' => get_class($this),
204
+ ]);
205
+ }
206
+
207
+ // 3) Update products for this category
208
+ $categoryRecord->setProducts($products);
209
+ }
210
+
211
+ return $this;
212
+ }
213
+ }
@@ -0,0 +1,118 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Models;
4
+
5
+ use Fleetbase\Models\Category;
6
+ use Illuminate\Database\Eloquent\Relations\BelongsTo;
7
+ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
8
+ use Illuminate\Database\Eloquent\Relations\MorphTo;
9
+ use Illuminate\Support\Str;
10
+
11
+ class CatalogCategory extends Category
12
+ {
13
+ /**
14
+ * The key to use in the payload responses (optional).
15
+ */
16
+ protected string $payloadKey = 'catalog_category';
17
+
18
+ /**
19
+ * Override the boot method to set "for" automatically.
20
+ */
21
+ protected static function boot()
22
+ {
23
+ parent::boot();
24
+
25
+ static::creating(function (Category $model) {
26
+ $model->for = 'storefront_catalog';
27
+ });
28
+ }
29
+
30
+ /**
31
+ * The catalog the category belongs to.
32
+ */
33
+ public function owner(): MorphTo
34
+ {
35
+ return $this->setConnection(config('storefront.connection.db'))->morphTo(__FUNCTION__, 'owner_type', 'owner_uuid');
36
+ }
37
+
38
+ /**
39
+ * The catalog the category belongs to.
40
+ */
41
+ public function catalog(): BelongsTo
42
+ {
43
+ return $this->setConnection(config('storefront.connection.db'))->belongsTo(Catalog::class, 'owner_uuid', 'uuid');
44
+ }
45
+
46
+ /**
47
+ * Many-to-many relationship with Product via the pivot table.
48
+ */
49
+ public function products(): BelongsToMany
50
+ {
51
+ return $this->setConnection(config('storefront.connection.db'))
52
+ ->belongsToMany(
53
+ Product::class,
54
+ 'catalog_category_products',
55
+ 'catalog_category_uuid',
56
+ 'product_uuid'
57
+ )
58
+ ->using(CatalogProduct::class)
59
+ ->withTimestamps()
60
+ ->wherePivotNull('deleted_at');
61
+ }
62
+
63
+ /**
64
+ * Update or create product relationships for this catalog category.
65
+ *
66
+ * This method:
67
+ * 1) Removes pivot entries (CatalogProduct) for products no longer in `$products`.
68
+ * 2) Creates pivot entries for new product IDs/UUIDs in `$products`.
69
+ *
70
+ * @param array $products an array of product identifiers (UUIDs) or objects containing a 'uuid' key
71
+ *
72
+ * @return $this
73
+ */
74
+ public function setProducts(array $products = []): CatalogCategory
75
+ {
76
+ // Ensure products relation is loaded if needed (optional).
77
+ $this->loadMissing('products');
78
+
79
+ // Fetch existing pivot records for this category
80
+ $existingPivotRecords = CatalogProduct::where('catalog_category_uuid', $this->uuid)->get();
81
+ $existingProductUuids = $existingPivotRecords->pluck('product_uuid')->toArray();
82
+
83
+ // Normalize incoming product UUIDs
84
+ $incomingProductUuids = collect($products)
85
+ ->map(function ($item) {
86
+ // If $item is simply a UUID string, use it
87
+ if (is_string($item) && Str::isUuid($item)) {
88
+ return $item;
89
+ }
90
+
91
+ // If $item is an array/object, try to extract 'uuid'
92
+ return data_get($item, 'uuid');
93
+ })
94
+ ->filter(fn ($uuid) => Str::isUuid($uuid)) // only valid UUIDs
95
+ ->unique()
96
+ ->values()
97
+ ->toArray();
98
+
99
+ // 1) Remove pivot rows for products not in incoming list
100
+ $toRemove = array_diff($existingProductUuids, $incomingProductUuids);
101
+ if (!empty($toRemove)) {
102
+ CatalogProduct::where('catalog_category_uuid', $this->uuid)
103
+ ->whereIn('product_uuid', $toRemove)
104
+ ->delete();
105
+ }
106
+
107
+ // 2) Create pivot rows for new products
108
+ $toAdd = array_diff($incomingProductUuids, $existingProductUuids);
109
+ foreach ($toAdd as $productUuid) {
110
+ CatalogProduct::create([
111
+ 'catalog_category_uuid' => $this->uuid,
112
+ 'product_uuid' => $productUuid,
113
+ ]);
114
+ }
115
+
116
+ return $this;
117
+ }
118
+ }
@@ -0,0 +1,46 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Models;
4
+
5
+ use Fleetbase\Traits\HasApiModelBehavior;
6
+ use Fleetbase\Traits\HasUuid;
7
+ use Illuminate\Database\Eloquent\Relations\BelongsTo;
8
+ use Illuminate\Database\Eloquent\SoftDeletes;
9
+
10
+ class CatalogHour extends StorefrontModel
11
+ {
12
+ use HasUuid;
13
+ use HasApiModelBehavior;
14
+ use SoftDeletes;
15
+
16
+ /**
17
+ * The database table used by the model.
18
+ *
19
+ * @var string
20
+ */
21
+ protected $table = 'catalog_hours';
22
+
23
+ /**
24
+ * The attributes that are mass assignable.
25
+ *
26
+ * @var array
27
+ */
28
+ protected $fillable = [
29
+ 'catalog_uuid',
30
+ 'day_of_week',
31
+ 'start',
32
+ 'end',
33
+ ];
34
+
35
+ /**
36
+ * The relationships that should always be loaded.
37
+ *
38
+ * @var array
39
+ */
40
+ protected $with = [];
41
+
42
+ public function catalog(): BelongsTo
43
+ {
44
+ return $this->belongsTo(Catalog::class, 'catalog_uuid', 'uuid');
45
+ }
46
+ }
@@ -0,0 +1,25 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Models;
4
+
5
+ use Fleetbase\Traits\HasUuid;
6
+ use Illuminate\Database\Eloquent\Relations\Pivot;
7
+ use Illuminate\Database\Eloquent\SoftDeletes;
8
+
9
+ class CatalogProduct extends Pivot
10
+ {
11
+ use SoftDeletes;
12
+ use HasUuid;
13
+
14
+ protected $connection = 'storefront';
15
+ protected $table = 'catalog_category_products';
16
+ protected $primaryKey = 'uuid';
17
+ public $incrementing = false;
18
+ protected $fillable = ['catalog_category_uuid', 'product_uuid'];
19
+
20
+ public function __construct(array $attributes = [])
21
+ {
22
+ $this->setConnection(config('storefront.connection.db'));
23
+ parent::__construct($attributes);
24
+ }
25
+ }
@@ -0,0 +1,70 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Models;
4
+
5
+ use Fleetbase\Traits\HasApiModelBehavior;
6
+ use Fleetbase\Traits\HasUuid;
7
+ use Illuminate\Database\Eloquent\Relations\BelongsTo;
8
+ use Illuminate\Database\Eloquent\Relations\MorphPivot;
9
+ use Illuminate\Database\Eloquent\Relations\MorphTo;
10
+ use Illuminate\Database\Eloquent\SoftDeletes;
11
+
12
+ class CatalogSubject extends MorphPivot
13
+ {
14
+ use HasUuid;
15
+ use HasApiModelBehavior;
16
+ use SoftDeletes;
17
+
18
+ /**
19
+ * The default database connection to use.
20
+ *
21
+ * @var string
22
+ */
23
+ protected $connection = 'storefront';
24
+
25
+ /**
26
+ * The table associated with the model.
27
+ *
28
+ * @var string
29
+ */
30
+ protected $table = 'catalog_subjects';
31
+
32
+ /**
33
+ * The attributes that are mass assignable.
34
+ *
35
+ * @var array
36
+ */
37
+ protected $fillable = [
38
+ 'uuid',
39
+ 'catalog_uuid',
40
+ 'subject_type',
41
+ 'subject_uuid',
42
+ 'company_uuid',
43
+ 'created_by_uuid',
44
+ ];
45
+
46
+ /**
47
+ * Set the configured connection.
48
+ */
49
+ public function __construct(array $attributes = [])
50
+ {
51
+ $this->setConnection(config('storefront.connection.db'));
52
+ parent::__construct($attributes);
53
+ }
54
+
55
+ /**
56
+ * The "subject" of this pivot - can be a Store, FoodTruck, etc.
57
+ */
58
+ public function subject(): MorphTo
59
+ {
60
+ return $this->morphTo(__FUNCTION__, 'subject_type', 'subject_uuid');
61
+ }
62
+
63
+ /**
64
+ * Get the associated catalog.
65
+ */
66
+ public function catalog(): BelongsTo
67
+ {
68
+ return $this->belongsTo(Catalog::class, 'catalog_uuid', 'uuid');
69
+ }
70
+ }