@fleetbase/storefront-engine 0.3.21 → 0.3.23

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.
@@ -59,3 +59,11 @@
59
59
  .status-badge.order-canceled-status-badge > span svg {
60
60
  color: #fca5a5;
61
61
  }
62
+
63
+ /** hotfix nav tab */
64
+ body[data-theme='dark'] .ui-tabs.overlay-content-panel > ul .nav-item.active,
65
+ body[data-theme='dark'] .ui-tabs.overlay-content-panel > nav .nav-item.active,
66
+ body[data-theme='dark'] .ui-tabs.overlay-content-panel > ul .ui-tab.active,
67
+ body[data-theme='dark'] .ui-tabs.overlay-content-panel > nav .ui-tab.active {
68
+ background-color: #202A37;
69
+ }
@@ -142,7 +142,7 @@
142
142
  <Button @text="New Variant" @icon="plus" @iconPrefix="fas" @onClick={{this.createProductVariant}} @permission="storefront create product-variant" />
143
143
  </div>
144
144
  </div>
145
- <Tabs as |Tab|>
145
+ <Tabs class="overlay-content-panel" as |Tab|>
146
146
  {{#each this.product.variants as |variant|}}
147
147
  <Tab @title={{variant.name}}>
148
148
  <div class="px-4 py-3">
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/storefront-api",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Storefront",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/storefront",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/storefront-engine",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "storefront",
@@ -0,0 +1,118 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Console\Commands;
4
+
5
+ use Fleetbase\FleetOps\Models\Order;
6
+ use Fleetbase\FleetOps\Support\Utils;
7
+ use Illuminate\Console\Command;
8
+
9
+ class SendOrderNotification extends Command
10
+ {
11
+ /**
12
+ * The name and signature of the console command.
13
+ *
14
+ * @var string
15
+ */
16
+ protected $signature = 'storefront:send-notification {--id= : The ID of the order} {--event= : The name of the event to trigger}';
17
+
18
+ /**
19
+ * The console command description.
20
+ *
21
+ * @var string
22
+ */
23
+ protected $description = 'Manually trigger a push notification for an order';
24
+
25
+ /**
26
+ * Mapping of event names to notification classes.
27
+ *
28
+ * @var array
29
+ */
30
+ protected $eventToNotification = [
31
+ 'created' => \Fleetbase\Storefront\Notifications\StorefrontOrderCreated::class,
32
+ 'canceled' => \Fleetbase\Storefront\Notifications\StorefrontOrderCanceled::class,
33
+ 'completed' => \Fleetbase\Storefront\Notifications\StorefrontOrderCompleted::class,
34
+ 'driver_assigned' => \Fleetbase\Storefront\Notifications\StorefrontOrderDriverAssigned::class,
35
+ 'enroute' => \Fleetbase\Storefront\Notifications\StorefrontOrderEnroute::class,
36
+ 'preparing' => \Fleetbase\Storefront\Notifications\StorefrontOrderPreparing::class,
37
+ 'ready_for_pickup' => \Fleetbase\Storefront\Notifications\StorefrontOrderReadyForPickup::class,
38
+ 'nearby' => \Fleetbase\Storefront\Notifications\StorefrontOrderNearby::class,
39
+ ];
40
+
41
+ /**
42
+ * Execute the console command.
43
+ *
44
+ * @return int
45
+ */
46
+ public function handle()
47
+ {
48
+ // Get order ID and event from options
49
+ $orderId = $this->option('id');
50
+ $event = $this->option('event');
51
+
52
+ // Prompt user to search for order ID if not provided
53
+ if (!$orderId) {
54
+ $orderId = $this->ask('Enter the order ID to trigger the notification');
55
+ }
56
+
57
+ // Attempt to find the order
58
+ $order = Order::where('public_id', $orderId)->first();
59
+
60
+ if (!$order) {
61
+ $this->error('Order not found!');
62
+
63
+ return 1;
64
+ }
65
+
66
+ // Load customer relation
67
+ $order->loadMissing('customer');
68
+
69
+ if (!$order->customer) {
70
+ $this->error('Order does not have an associated customer!');
71
+
72
+ return 1;
73
+ }
74
+
75
+ // Prompt user to select event if not provided
76
+ if (!$event) {
77
+ $event = $this->choice(
78
+ 'Select the event to trigger',
79
+ array_keys($this->eventToNotification),
80
+ 'created' // Default event
81
+ );
82
+ }
83
+
84
+ // Resolve notification class
85
+ $notificationClass = $this->eventToNotification[$event] ?? null;
86
+
87
+ if (!$notificationClass) {
88
+ $this->error('Invalid event selected!');
89
+
90
+ return 1;
91
+ }
92
+
93
+ // nearby notification requires more arguments
94
+ try {
95
+ if ($event === 'nearby') {
96
+ $origin = $order->payload->getPickupOrFirstWaypoint();
97
+ $destination = $order->payload->getDropoffOrLastWaypoint();
98
+ $matrix = Utils::getDrivingDistanceAndTime($origin, $destination);
99
+ $distance = $matrix->distance;
100
+ $time = $matrix->time;
101
+
102
+ // Trigger notification
103
+ $order->customer->notify(new $notificationClass($order, $distance, $time));
104
+ } else {
105
+ // Trigger notification
106
+ $order->customer->notify(new $notificationClass($order));
107
+ }
108
+ } catch (\Exception $e) {
109
+ $this->error($e->getMessage());
110
+
111
+ return 0;
112
+ }
113
+
114
+ $this->info("Notification '{$event}' has been triggered for order ID '{$orderId}'.");
115
+
116
+ return 0;
117
+ }
118
+ }
@@ -10,7 +10,6 @@ use Fleetbase\Storefront\Imports\ProductsImport;
10
10
  use Fleetbase\Storefront\Jobs\DownloadProductImageUrl;
11
11
  use Fleetbase\Storefront\Models\Product;
12
12
  use Fleetbase\Storefront\Models\Store;
13
- use Fleetbase\Support\Http;
14
13
  use Illuminate\Http\Request;
15
14
  use Illuminate\Support\Arr;
16
15
  use Illuminate\Support\Str;
@@ -25,53 +24,6 @@ class ProductController extends StorefrontController
25
24
  */
26
25
  public $resource = 'product';
27
26
 
28
- /**
29
- * Update a Product record.
30
- * This update method was overwritten because the ProductObserver
31
- * isn't firing on the `updated` callback.
32
- *
33
- * @return \Fleetbase\Storefront\Http\Resources\Product
34
- */
35
- public function updateRecord(Request $request, string $id)
36
- {
37
- try {
38
- $this->validateRequest($request);
39
- $record = $this->model->updateRecordFromRequest($request, $id, function (&$request, Product &$product) {
40
- $addonCategories = $request->array('product.addon_categories');
41
- $variants = $request->array('product.variants');
42
- $files = $request->array('product.files');
43
-
44
- // dd($addonCategories);
45
-
46
- // save addon categories
47
- $product->setAddonCategories($addonCategories);
48
-
49
- // save product variants
50
- $product->setProductVariants($variants);
51
-
52
- // set keys on files
53
- foreach ($files as $file) {
54
- $fileRecord = File::where('uuid', $file['uuid'])->first();
55
- $fileRecord->setKey($product);
56
- }
57
- });
58
-
59
- if (Http::isInternalRequest($request)) {
60
- $this->resource::wrap($this->resourceSingularlName);
61
-
62
- return new $this->resource($record);
63
- }
64
-
65
- return new $this->resource($record);
66
- } catch (\Exception $e) {
67
- return response()->error($e->getMessage());
68
- } catch (\Illuminate\Database\QueryException $e) {
69
- return response()->error($e->getMessage());
70
- } catch (\Fleetbase\Exceptions\FleetbaseRequestValidationException $e) {
71
- return response()->error($e->getErrors());
72
- }
73
- }
74
-
75
27
  /**
76
28
  * List all activity options for current order.
77
29
  *
@@ -186,7 +186,7 @@ class CheckoutController extends Controller
186
186
 
187
187
  // Check if customer has a saved default payment method
188
188
  if (StripeUtils::isCustomerPaymentMethodValid($customer)) {
189
- $paymentIntentData['payment_method'] = $$customer->getMeta('stripe_payment_method_id');
189
+ $paymentIntentData['payment_method'] = $customer->getMeta('stripe_payment_method_id');
190
190
  }
191
191
 
192
192
  try {
@@ -246,7 +246,7 @@ class CheckoutController extends Controller
246
246
 
247
247
  // Check if customer has a saved default payment method
248
248
  if (StripeUtils::isCustomerPaymentMethodValid($customer)) {
249
- $paymentIntentData['payment_method'] = $$customer->getMeta('stripe_payment_method_id');
249
+ $paymentIntentData['payment_method'] = $customer->getMeta('stripe_payment_method_id');
250
250
  }
251
251
 
252
252
  try {
@@ -2,6 +2,8 @@
2
2
 
3
3
  namespace Fleetbase\Storefront\Http\Controllers\v1;
4
4
 
5
+ use Fleetbase\Auth\AppleVerifier;
6
+ use Fleetbase\Auth\GoogleVerifier;
5
7
  use Fleetbase\FleetOps\Exceptions\UserAlreadyExistsException;
6
8
  use Fleetbase\FleetOps\Http\Requests\UpdateContactRequest;
7
9
  use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
@@ -36,7 +38,7 @@ class CustomerController extends Controller
36
38
  $customer = Storefront::getCustomerFromToken();
37
39
 
38
40
  if (!$customer) {
39
- return response()->error('Not authorized to register device for cutomer');
41
+ return response()->apiError('Not authorized to register device for cutomer');
40
42
  }
41
43
 
42
44
  $device = UserDevice::firstOrCreate(
@@ -68,7 +70,7 @@ class CustomerController extends Controller
68
70
  $customer = Storefront::getCustomerFromToken();
69
71
 
70
72
  if (!$customer) {
71
- return response()->error('Not authorized to view customers orders');
73
+ return response()->apiError('Not authorized to view customers orders');
72
74
  }
73
75
 
74
76
  $results = Order::queryWithRequest($request, function (&$query) use ($customer) {
@@ -96,7 +98,7 @@ class CustomerController extends Controller
96
98
  $customer = Storefront::getCustomerFromToken();
97
99
 
98
100
  if (!$customer) {
99
- return response()->error('Not authorized to view customers places');
101
+ return response()->apiError('Not authorized to view customers places');
100
102
  }
101
103
 
102
104
  $results = Place::queryWithRequest($request, function (&$query) use ($customer) {
@@ -121,7 +123,7 @@ class CustomerController extends Controller
121
123
 
122
124
  // validate identity
123
125
  if ($mode === 'email' && !$isEmail) {
124
- return response()->error('Invalid email provided for identity');
126
+ return response()->apiError('Invalid email provided for identity');
125
127
  }
126
128
 
127
129
  // prepare phone number
@@ -176,7 +178,7 @@ class CustomerController extends Controller
176
178
  // verify code
177
179
  $isVerified = VerificationCode::where(['code' => $code, 'for' => 'storefront_create_customer', 'meta->identity' => $identity])->exists();
178
180
  if (!$isVerified) {
179
- return response()->error('Invalid verification code provided!');
181
+ return response()->apiError('Invalid verification code provided!');
180
182
  }
181
183
 
182
184
  // check for existing user to attach contact to
@@ -250,7 +252,7 @@ class CustomerController extends Controller
250
252
  try {
251
253
  $contact = Contact::findRecordOrFail($id);
252
254
  } catch (ModelNotFoundException $exception) {
253
- return response()->error('Customer resource not found.');
255
+ return response()->apiError('Customer resource not found.');
254
256
  }
255
257
 
256
258
  // get request input
@@ -303,7 +305,7 @@ class CustomerController extends Controller
303
305
  try {
304
306
  $contact = Contact::findRecordOrFail($id);
305
307
  } catch (ModelNotFoundException $exception) {
306
- return response()->error('Customer resource not found.');
308
+ return response()->apiError('Customer resource not found.');
307
309
  }
308
310
 
309
311
  // response the customer resource
@@ -325,7 +327,7 @@ class CustomerController extends Controller
325
327
  try {
326
328
  $contact = Contact::findRecordOrFail($id);
327
329
  } catch (ModelNotFoundException $exception) {
328
- return response()->error('Customer resource not found.');
330
+ return response()->apiError('Customer resource not found.');
329
331
  }
330
332
 
331
333
  // delete the product
@@ -349,7 +351,7 @@ class CustomerController extends Controller
349
351
  $user = User::where('email', $identity)->orWhere('phone', static::phone($identity))->first();
350
352
 
351
353
  if (!Hash::check($password, $user->password)) {
352
- return response()->error('Authentication failed using password provided.', 401);
354
+ return response()->apiError('Authentication failed using password provided.', 401);
353
355
  }
354
356
 
355
357
  // get the storefront or network logging in for
@@ -376,7 +378,7 @@ class CustomerController extends Controller
376
378
  try {
377
379
  $token = $user->createToken($contact->uuid);
378
380
  } catch (\Exception $e) {
379
- return response()->error($e->getMessage());
381
+ return response()->apiError($e->getMessage());
380
382
  }
381
383
 
382
384
  $contact->token = $token->plainTextToken;
@@ -397,7 +399,7 @@ class CustomerController extends Controller
397
399
  $user = User::where('phone', $phone)->whereNull('deleted_at')->withoutGlobalScopes()->first();
398
400
 
399
401
  if (!$user) {
400
- return response()->error('No customer with this phone # found.');
402
+ return response()->apiError('No customer with this phone # found.');
401
403
  }
402
404
 
403
405
  // get the storefront or network logging in for
@@ -413,6 +415,243 @@ class CustomerController extends Controller
413
415
  return response()->json(['status' => 'OK']);
414
416
  }
415
417
 
418
+ /**
419
+ * Handles user authentication via Apple Sign-In.
420
+ *
421
+ * This method validates the Apple ID token, checks if the user exists in the system,
422
+ * and creates a new user if necessary. It then ensures a contact record exists for
423
+ * the user and generates an authentication token.
424
+ *
425
+ * @param Request $request
426
+ * The HTTP request containing the following required fields:
427
+ * - `identityToken` (string): The token generated by Apple to identify the user.
428
+ * - `authorizationCode` (string): The one-time code issued by Apple during login.
429
+ * - `email` (string|null): The user's email address (provided on first login).
430
+ * - `phone` (string|null): The user's phone number (optional).
431
+ * - `name` (string|null): The user's full name (optional).
432
+ * - `appleUserId` (string): A unique identifier for the user assigned by Apple.
433
+ *
434
+ * @return \Illuminate\Http\JsonResponse
435
+ * A JSON response containing the authenticated customer's details, including an access token
436
+ *
437
+ * @throws \Exception
438
+ * If Apple authentication fails or any other error occurs during the process
439
+ */
440
+ public function loginWithApple(Request $request)
441
+ {
442
+ $identityToken = $request->input('identityToken');
443
+ $authorizationCode = $request->input('authorizationCode');
444
+ $email = $request->input('email');
445
+ $phone = $request->input('phone');
446
+ $name = $request->input('name');
447
+ $appleUserId = $request->input('appleUserId');
448
+
449
+ if (!$identityToken || !$authorizationCode) {
450
+ return response()->apiError('Missing required Apple authentication parameters.', 400);
451
+ }
452
+
453
+ try {
454
+ // Verify the Apple token using the utility function
455
+ $isValid = AppleVerifier::verifyAppleJwt($identityToken);
456
+ if (!$isValid) {
457
+ return response()->apiError('Apple ID authentication is not valid.', 400);
458
+ }
459
+
460
+ // Check if the user exists in the system
461
+ $user = User::where(function ($query) use ($email, $appleUserId) {
462
+ if ($email) {
463
+ $query->where('email', $email);
464
+ $query->orWhere('apple_user_id', $appleUserId);
465
+ } else {
466
+ $query->where('apple_user_id', $appleUserId);
467
+ }
468
+ })->first();
469
+
470
+ if (!$user) {
471
+ // Create a new user
472
+ $user = User::create([
473
+ 'email' => $email,
474
+ 'phone' => $phone,
475
+ 'name' => $name,
476
+ 'apple_user_id' => $appleUserId,
477
+ 'type' => 'customer',
478
+ 'company_uuid' => session('company'),
479
+ ]);
480
+ } else {
481
+ // Update the `apple_user_id` if it's not already set
482
+ if (!$user->apple_user_id) {
483
+ $user->apple_user_id = $appleUserId;
484
+ $user->save();
485
+ }
486
+ }
487
+
488
+ // Ensure a customer contact exists
489
+ $contact = Contact::firstOrCreate(
490
+ ['user_uuid' => $user->uuid, 'company_uuid' => session('company')],
491
+ ['name' => $user->name, 'email' => $user->email, 'phone' => $user->phone, 'meta' => ['apple_user_id' => $appleUserId], 'type' => 'customer']
492
+ );
493
+
494
+ // Generate an auth token
495
+ $token = $user->createToken($contact->uuid);
496
+ $contact->token = $token->plainTextToken;
497
+
498
+ return new Customer($contact);
499
+ } catch (\Exception $e) {
500
+ return response()->apiError($e->getMessage(), 500);
501
+ }
502
+ }
503
+
504
+ /**
505
+ * Handles user authentication via Facebook Sign-In.
506
+ *
507
+ * This method checks if the user exists in the system based on their email or Facebook ID.
508
+ * If the user does not exist, it creates a new user and ensures a contact record is created.
509
+ * Finally, it generates an authentication token for the user.
510
+ *
511
+ * @param Request $request
512
+ * The HTTP request containing the following required fields:
513
+ * - `email` (string|null): The user's email address.
514
+ * - `name` (string|null): The user's full name.
515
+ * - `facebookUserId` (string): A unique identifier for the user assigned by Facebook.
516
+ *
517
+ * @return \Illuminate\Http\JsonResponse
518
+ * A JSON response containing the authenticated customer's details, including an access token
519
+ *
520
+ * @throws \Exception
521
+ * If Facebook authentication fails or any other error occurs during the process
522
+ */
523
+ public function loginWithFacebook(Request $request)
524
+ {
525
+ $email = $request->input('email');
526
+ $name = $request->input('name');
527
+ $facebookUserId = $request->input('facebookUserId');
528
+
529
+ try {
530
+ // Check if the user exists in the system
531
+ $user = User::where(function ($query) use ($email, $facebookUserId) {
532
+ if ($email) {
533
+ $query->where('email', $email);
534
+ $query->orWhere('facebook_user_id', $facebookUserId);
535
+ } else {
536
+ $query->where('facebook_user_id', $facebookUserId);
537
+ }
538
+ })->first();
539
+
540
+ if (!$user) {
541
+ // Create a new user
542
+ $user = User::create([
543
+ 'email' => $email,
544
+ 'name' => $name,
545
+ 'facebook_user_id' => $facebookUserId,
546
+ 'type' => 'customer',
547
+ 'company_uuid' => session('company'),
548
+ ]);
549
+ } else {
550
+ // Update the `facebook_user_id` if it's not already set
551
+ if (!$user->facebook_user_id) {
552
+ $user->facebook_user_id = $facebookUserId;
553
+ $user->save();
554
+ }
555
+ }
556
+
557
+ // Ensure a customer contact exists
558
+ $contact = Contact::firstOrCreate(
559
+ ['user_uuid' => $user->uuid, 'company_uuid' => session('company')],
560
+ ['name' => $user->name, 'email' => $user->email, 'phone' => $user->phone, 'meta' => ['facebook_user_id' => $facebookUserId], 'type' => 'customer']
561
+ );
562
+
563
+ // Generate an auth token
564
+ $token = $user->createToken($contact->uuid);
565
+ $contact->token = $token->plainTextToken;
566
+
567
+ return new Customer($contact);
568
+ } catch (\Exception $e) {
569
+ return response()->apiError($e->getMessage(), 500);
570
+ }
571
+ }
572
+
573
+ /**
574
+ * Handles user authentication via Google Sign-In.
575
+ *
576
+ * This method validates the Google ID token, retrieves user details from the token payload,
577
+ * checks if the user exists in the system, and creates a new user if necessary.
578
+ * It ensures a contact record exists for the user and generates an authentication token.
579
+ *
580
+ * @param Request $request
581
+ * The HTTP request containing the following required fields:
582
+ * - `idToken` (string): The token generated by Google to identify the user.
583
+ * - `clientId` (string): The client ID associated with the app.
584
+ *
585
+ * @return \Illuminate\Http\JsonResponse
586
+ * A JSON response containing the authenticated customer's details, including an access token
587
+ *
588
+ * @throws \Exception
589
+ * If Google authentication fails or any other error occurs during the process
590
+ */
591
+ public function loginWithGoogle(Request $request)
592
+ {
593
+ $idToken = $request->input('idToken');
594
+ $clientId = $request->input('clientId');
595
+ if (!$idToken || !$clientId) {
596
+ return response()->apiError('Missing required Google authentication parameters.', 400);
597
+ }
598
+
599
+ try {
600
+ // Verify the Google ID token using the utility function
601
+ $payload = GoogleVerifier::verifyIdToken($idToken, $clientId);
602
+ if (!$payload) {
603
+ return response()->apiError('Google Sign-In authentication is not valid.', 400);
604
+ }
605
+
606
+ // Extract user details from the payload
607
+ $email = data_get($payload, 'email');
608
+ $name = data_get($payload, 'name');
609
+ $googleUserId = data_get($payload, 'sub');
610
+ $avatarUrl = data_get($payload, 'picture');
611
+
612
+ // Check if the user exists in the system
613
+ $user = User::where(function ($query) use ($email, $googleUserId) {
614
+ if ($email) {
615
+ $query->where('email', $email);
616
+ $query->orWhere('google_user_id', $googleUserId);
617
+ } else {
618
+ $query->where('google_user_id', $googleUserId);
619
+ }
620
+ })->first();
621
+
622
+ if (!$user) {
623
+ // Create a new user
624
+ $user = User::create([
625
+ 'email' => $email,
626
+ 'name' => $name,
627
+ 'google_user_id' => $googleUserId,
628
+ 'type' => 'customer',
629
+ 'company_uuid' => session('company'),
630
+ ]);
631
+ } else {
632
+ // Update the `google_user_id` if it's not already set
633
+ if (!$user->google_user_id) {
634
+ $user->google_user_id = $googleUserId;
635
+ $user->save();
636
+ }
637
+ }
638
+
639
+ // Ensure a customer contact exists
640
+ $contact = Contact::firstOrCreate(
641
+ ['user_uuid' => $user->uuid, 'company_uuid' => session('company')],
642
+ ['name' => $user->name, 'email' => $user->email, 'phone' => $user->phone, 'meta' => ['google_user_id' => $googleUserId], 'type' => 'customer']
643
+ );
644
+
645
+ // Generate an auth token
646
+ $token = $user->createToken($contact->uuid);
647
+ $contact->token = $token->plainTextToken;
648
+
649
+ return new Customer($contact);
650
+ } catch (\Exception $e) {
651
+ return response()->apiError($e->getMessage(), 500);
652
+ }
653
+ }
654
+
416
655
  /**
417
656
  * Verifys SMS code and sends auth token with customer resource.
418
657
  *
@@ -433,13 +672,13 @@ class CustomerController extends Controller
433
672
  $user = User::where('phone', $identity)->orWhere('email', $identity)->first();
434
673
 
435
674
  if (!$user) {
436
- return response()->error('Unable to verify code.');
675
+ return response()->apiError('Unable to verify code.');
437
676
  }
438
677
 
439
678
  // find and verify code
440
679
  $verificationCode = VerificationCode::where(['subject_uuid' => $user->uuid, 'code' => $code, 'for' => $for])->exists();
441
680
  if (!$verificationCode && $code !== config('storefront.storefront_app.bypass_verification_code')) {
442
- return response()->error('Invalid verification code!');
681
+ return response()->apiError('Invalid verification code!');
443
682
  }
444
683
 
445
684
  // get the storefront or network logging in for
@@ -466,7 +705,7 @@ class CustomerController extends Controller
466
705
  try {
467
706
  $token = $user->createToken($contact->uuid);
468
707
  } catch (\Exception $e) {
469
- return response()->error($e->getMessage());
708
+ return response()->apiError($e->getMessage());
470
709
  }
471
710
 
472
711
  $contact->token = $token->plainTextToken;
@@ -494,7 +733,7 @@ class CustomerController extends Controller
494
733
  {
495
734
  $customer = Storefront::getCustomerFromToken();
496
735
  if (!$customer) {
497
- return response()->error('Not authorized to view customers places');
736
+ return response()->apiError('Not authorized to view customers places');
498
737
  }
499
738
 
500
739
  $gateway = Storefront::findGateway('stripe');
@@ -529,7 +768,7 @@ class CustomerController extends Controller
529
768
  {
530
769
  $customer = Storefront::getCustomerFromToken();
531
770
  if (!$customer) {
532
- return response()->error('Not authorized to view customers places');
771
+ return response()->apiError('Not authorized to view customers places');
533
772
  }
534
773
 
535
774
  $gateway = Storefront::findGateway('stripe');
@@ -5,7 +5,7 @@ namespace Fleetbase\Storefront\Http\Requests;
5
5
  use Fleetbase\Http\Requests\FleetbaseRequest;
6
6
  use Fleetbase\Storefront\Rules\CustomerExists;
7
7
 
8
- class CustomerRequest extends FleetbaseRequest
8
+ class StorefrontCustomerRequest extends FleetbaseRequest
9
9
  {
10
10
  /**
11
11
  * Determine if the user is authorized to make this request.
@@ -6,6 +6,7 @@ use Fleetbase\Http\Resources\FleetbaseResource;
6
6
  use Fleetbase\Support\Http;
7
7
  use Illuminate\Support\Arr;
8
8
  use Illuminate\Support\Str;
9
+ use Illuminate\Support\Collection;
9
10
 
10
11
  class Product extends FleetbaseResource
11
12
  {
@@ -55,7 +56,7 @@ class Product extends FleetbaseResource
55
56
  ];
56
57
  }
57
58
 
58
- public function mapHours(?\Illuminate\Database\Eloquent\Collection $hours = null): array
59
+ public function mapHours(Collection|array $hours = []): array
59
60
  {
60
61
  if (empty($hours)) {
61
62
  return [];
@@ -79,7 +80,7 @@ class Product extends FleetbaseResource
79
80
  );
80
81
  }
81
82
 
82
- public function mapFiles(?\Illuminate\Database\Eloquent\Collection $files = null, $contentType = 'image')
83
+ public function mapFiles(Collection|array $files = [], $contentType = 'image')
83
84
  {
84
85
  return collect($files)->map(function ($file) use ($contentType) {
85
86
  if (!Str::contains($file->content_type, $contentType)) {
@@ -90,7 +91,7 @@ class Product extends FleetbaseResource
90
91
  })->filter()->values();
91
92
  }
92
93
 
93
- public function mapAddonCategories(?\Illuminate\Database\Eloquent\Collection $addonCategories = null)
94
+ public function mapAddonCategories(Collection|array $addonCategories = [])
94
95
  {
95
96
  return collect($addonCategories)->map(function ($addonCategory) {
96
97
  $addons = data_get($addonCategory, 'category.addons', []);
@@ -122,7 +123,7 @@ class Product extends FleetbaseResource
122
123
  });
123
124
  }
124
125
 
125
- public function mapProductAddons(?\Illuminate\Database\Eloquent\Collection $addons = null, $excluded = [])
126
+ public function mapProductAddons(Collection|array $addons = [], $excluded = [])
126
127
  {
127
128
  return collect($addons)->map(function ($addon) use ($excluded) {
128
129
  if (is_array($excluded) && in_array($addon->uuid, $excluded)) {
@@ -159,7 +160,7 @@ class Product extends FleetbaseResource
159
160
  })->filter()->values();
160
161
  }
161
162
 
162
- public function mapVariants(?\Illuminate\Database\Eloquent\Collection $variants = null)
163
+ public function mapVariants(Collection|array $variants = [])
163
164
  {
164
165
  return collect($variants)->map(function ($variant) {
165
166
  $productVariantArr = [
@@ -2,15 +2,9 @@
2
2
 
3
3
  namespace Fleetbase\Storefront\Observers;
4
4
 
5
- use Fleetbase\FleetOps\Support\Utils;
6
5
  use Fleetbase\Models\File;
7
6
  use Fleetbase\Storefront\Models\Product;
8
- use Fleetbase\Storefront\Models\ProductAddonCategory;
9
- use Fleetbase\Storefront\Models\ProductVariant;
10
- use Fleetbase\Storefront\Models\ProductVariantOption;
11
- use Illuminate\Support\Arr;
12
7
  use Illuminate\Support\Facades\Request;
13
- use Illuminate\Support\Str;
14
8
 
15
9
  class ProductObserver
16
10
  {
@@ -47,56 +41,18 @@ class ProductObserver
47
41
  {
48
42
  $addonCategories = Request::input('product.addon_categories', []);
49
43
  $variants = Request::input('product.variants', []);
44
+ $files = Request::input('product.files', []);
50
45
 
51
- // update addon categories
52
- foreach ($addonCategories as $productAddonCategory) {
53
- if (isset($productAddonCategory['uuid']) && Str::isUuid($productAddonCategory['uuid'])) {
54
- ProductAddonCategory::where('uuid', $productAddonCategory['uuid'])->update(Arr::except($productAddonCategory, ['uuid', 'name', 'category']));
55
- continue;
56
- }
57
-
58
- // add new addon category
59
- $productAddonCategory['product_uuid'] = $product->uuid;
60
- ProductAddonCategory::create(Arr::except($productAddonCategory, ['category']));
61
- }
62
-
63
- // update product variants
64
- foreach ($variants as $variant) {
65
- if (isset($variant['uuid']) && Str::isUuid($variant['uuid'])) {
66
- // update product variante
67
- ProductVariant::where('uuid', $variant['uuid'])->update(Arr::except($variant, ['uuid', 'options']));
68
-
69
- // update product variant options
70
- foreach ($variant['options'] as $option) {
71
- if (!empty($option['uuid'])) {
72
- // make sure additional cost is always numbers only
73
- if (isset($option['additional_cost'])) {
74
- $option['additional_cost'] = Utils::numbersOnly($option['additional_cost']);
75
- }
76
-
77
- $updateAttrs = Arr::except($option, ['uuid']);
78
-
79
- ProductVariantOption::where('uuid', $option['uuid'])->update($updateAttrs);
80
- continue;
81
- }
82
-
83
- $option['product_variant_uuid'] = $variant['uuid'];
84
- ProductVariantOption::create($option);
85
- }
86
- continue;
87
- }
88
-
89
- // create new variant
90
- $variant['created_by_uuid'] = Request::session()->get('user');
91
- $variant['company_uuid'] = Request::session()->get('company');
92
- $variant['product_uuid'] = $product->uuid;
46
+ // save addon categories
47
+ $product->setAddonCategories($addonCategories);
93
48
 
94
- $productVariant = ProductVariant::create(Arr::except($variant, ['options']));
49
+ // save product variants
50
+ $product->setProductVariants($variants);
95
51
 
96
- foreach ($variant['options'] as $option) {
97
- $option['product_variant_uuid'] = $productVariant->uuid;
98
- ProductVariantOption::create($option);
99
- }
52
+ // set keys on files
53
+ foreach ($files as $file) {
54
+ $fileRecord = File::where('uuid', $file['uuid'])->first();
55
+ $fileRecord->setKey($product);
100
56
  }
101
57
  }
102
58
  }
@@ -54,6 +54,7 @@ class StorefrontServiceProvider extends CoreServiceProvider
54
54
  */
55
55
  public $commands = [
56
56
  \Fleetbase\Storefront\Console\Commands\NotifyStorefrontOrderNearby::class,
57
+ \Fleetbase\Storefront\Console\Commands\SendOrderNotification::class,
57
58
  ];
58
59
 
59
60
  /**
@@ -20,7 +20,7 @@ Route::prefix(config('storefront.api.routing.prefix', 'storefront'))->namespace(
20
20
  | Public/Callback Receivable Storefront API Routes
21
21
  |--------------------------------------------------------------------------
22
22
  |
23
- | End-user API routes, these are routes that the SDK and applications will interface with, and require API credentials.
23
+ | End-user API routes, these are routes that the SDK and applications will interface with, and DO NOT require API credentials.
24
24
  */
25
25
  Route::group(['prefix' => 'v1', 'namespace' => 'v1'], function ($router) {
26
26
  // storefront/v1/checkouts
@@ -94,6 +94,9 @@ Route::prefix(config('storefront.api.routing.prefix', 'storefront'))->namespace(
94
94
  $router->get('{id}', 'CustomerController@find');
95
95
  $router->post('/', 'CustomerController@create');
96
96
  $router->post('login-with-sms', 'CustomerController@loginWithPhone');
97
+ $router->post('login-with-apple', 'CustomerController@loginWithApple');
98
+ $router->post('login-with-facebook', 'CustomerController@loginWithFacebook');
99
+ $router->post('login-with-google', 'CustomerController@loginWithGoogle');
97
100
  $router->post('verify-code', 'CustomerController@verifyCode');
98
101
  $router->post('login', 'CustomerController@login');
99
102
  $router->post('request-creation-code', 'CustomerController@requestCustomerCreationCode');