@fleetbase/storefront-engine 0.1.7 → 0.1.8

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 (307) hide show
  1. package/.php-cs-fixer.php +29 -0
  2. package/LICENSE.md +16 -4
  3. package/README.md +106 -13
  4. package/app/adapters/addon-category.js +1 -1
  5. package/app/adapters/gateway.js +1 -1
  6. package/app/adapters/network.js +1 -1
  7. package/app/adapters/notification-channel.js +1 -1
  8. package/app/adapters/product-addon-category.js +1 -1
  9. package/app/adapters/product-addon.js +1 -1
  10. package/app/adapters/product-hour.js +1 -1
  11. package/app/adapters/product-store-location.js +1 -1
  12. package/app/adapters/product-variant-option.js +1 -1
  13. package/app/adapters/product-variant.js +1 -1
  14. package/app/adapters/product.js +1 -1
  15. package/app/adapters/store-hour.js +1 -1
  16. package/app/adapters/store-location.js +1 -1
  17. package/app/adapters/store.js +1 -1
  18. package/app/adapters/storefront.js +1 -1
  19. package/app/components/file-record.js +1 -1
  20. package/app/components/modals/add-store-hours.js +1 -1
  21. package/app/components/modals/add-store-to-category.js +1 -1
  22. package/app/components/modals/add-stores-to-network.js +1 -1
  23. package/app/components/modals/assign-driver.js +1 -1
  24. package/app/components/modals/create-first-store.js +1 -1
  25. package/app/components/modals/create-gateway.js +1 -1
  26. package/app/components/modals/create-network-category.js +1 -1
  27. package/app/components/modals/create-network.js +1 -1
  28. package/app/components/modals/create-new-variant.js +1 -1
  29. package/app/components/modals/create-notification-channel.js +1 -1
  30. package/app/components/modals/create-product-category.js +1 -1
  31. package/app/components/modals/create-store.js +1 -1
  32. package/app/components/modals/edit-network.js +1 -1
  33. package/app/components/modals/import-products.js +1 -1
  34. package/app/components/modals/incoming-order.js +1 -1
  35. package/app/components/modals/manage-addons.js +1 -1
  36. package/app/components/modals/order-ready-assign-driver.js +1 -1
  37. package/app/components/modals/select-addon-category.js +1 -1
  38. package/app/components/modals/share-network.js +1 -1
  39. package/app/components/modals/store-details.js +1 -1
  40. package/app/components/modals/store-form.js +1 -1
  41. package/app/components/modals/store-location-form.js +1 -1
  42. package/app/components/network-category-picker.js +1 -1
  43. package/app/components/order-card.js +1 -1
  44. package/app/components/schedule-manager.js +1 -1
  45. package/app/components/settings-container.js +1 -1
  46. package/app/components/store-selector.js +1 -1
  47. package/app/components/widget/customers.js +1 -1
  48. package/app/components/widget/orders.js +1 -1
  49. package/app/components/widget/storefront-metrics.js +1 -1
  50. package/app/controllers/application.js +1 -1
  51. package/app/controllers/customers/index.js +1 -1
  52. package/app/controllers/home.js +1 -1
  53. package/app/controllers/networks/index/network/customers.js +1 -1
  54. package/app/controllers/networks/index/network/index.js +1 -1
  55. package/app/controllers/networks/index/network/orders.js +1 -1
  56. package/app/controllers/networks/index/network/stores.js +1 -1
  57. package/app/controllers/networks/index/network.js +1 -1
  58. package/app/controllers/networks/index.js +1 -1
  59. package/app/controllers/orders/index.js +1 -1
  60. package/app/controllers/products/index/category/edit.js +1 -1
  61. package/app/controllers/products/index/category/new.js +1 -1
  62. package/app/controllers/products/index/category.js +1 -1
  63. package/app/controllers/products/index/index/edit.js +1 -1
  64. package/app/controllers/products/index/index.js +1 -1
  65. package/app/controllers/products/index.js +1 -1
  66. package/app/controllers/settings/api.js +1 -1
  67. package/app/controllers/settings/gateways.js +1 -1
  68. package/app/controllers/settings/index.js +1 -1
  69. package/app/controllers/settings/locations.js +1 -1
  70. package/app/controllers/settings/notifications.js +1 -1
  71. package/app/helpers/get-tip-amount.js +1 -1
  72. package/app/models/addon-category.js +1 -1
  73. package/app/models/gateway.js +1 -1
  74. package/app/models/network.js +1 -1
  75. package/app/models/notification-channel.js +1 -1
  76. package/app/models/product-addon-category.js +1 -1
  77. package/app/models/product-addon.js +1 -1
  78. package/app/models/product-hour.js +1 -1
  79. package/app/models/product-store-location.js +1 -1
  80. package/app/models/product-variant-option.js +1 -1
  81. package/app/models/product-variant.js +1 -1
  82. package/app/models/product.js +1 -1
  83. package/app/models/store-hour.js +1 -1
  84. package/app/models/store-location.js +1 -1
  85. package/app/models/store.js +1 -1
  86. package/app/routes/application.js +1 -1
  87. package/app/routes/customers/index/edit.js +1 -1
  88. package/app/routes/customers/index.js +1 -1
  89. package/app/routes/home.js +1 -1
  90. package/app/routes/networks/index/network/customers.js +1 -1
  91. package/app/routes/networks/index/network/index.js +1 -1
  92. package/app/routes/networks/index/network/orders.js +1 -1
  93. package/app/routes/networks/index/network/stores.js +1 -1
  94. package/app/routes/networks/index/network.js +1 -1
  95. package/app/routes/networks/index.js +1 -1
  96. package/app/routes/orders/index/edit.js +1 -1
  97. package/app/routes/orders/index/new.js +1 -1
  98. package/app/routes/orders/index/view.js +1 -1
  99. package/app/routes/orders/index.js +1 -1
  100. package/app/routes/products/index/category/edit.js +1 -1
  101. package/app/routes/products/index/category/new.js +1 -1
  102. package/app/routes/products/index/category.js +1 -1
  103. package/app/routes/products/index/index/edit.js +1 -1
  104. package/app/routes/products/index/index.js +1 -1
  105. package/app/routes/products/index.js +1 -1
  106. package/app/routes/settings/api.js +1 -1
  107. package/app/routes/settings/gateways.js +1 -1
  108. package/app/routes/settings/index.js +1 -1
  109. package/app/routes/settings/locations.js +1 -1
  110. package/app/routes/settings/notifications.js +1 -1
  111. package/app/serializers/addon-category.js +1 -1
  112. package/app/serializers/network.js +1 -1
  113. package/app/serializers/notification-channel.js +1 -1
  114. package/app/serializers/product-addon-category.js +1 -1
  115. package/app/serializers/product-variant.js +1 -1
  116. package/app/serializers/product.js +1 -1
  117. package/app/serializers/store-location.js +1 -1
  118. package/app/serializers/store.js +1 -1
  119. package/app/services/storefront.js +1 -1
  120. package/app/templates/customers/index/edit.js +1 -1
  121. package/app/templates/customers/index.js +1 -1
  122. package/app/templates/home.js +1 -1
  123. package/app/templates/networks/index/network/customers.js +1 -1
  124. package/app/templates/networks/index/network/index.js +1 -1
  125. package/app/templates/networks/index/network/orders.js +1 -1
  126. package/app/templates/networks/index/network/stores.js +1 -1
  127. package/app/templates/networks/index/network.js +1 -1
  128. package/app/templates/networks/index.js +1 -1
  129. package/app/templates/orders/index/edit.js +1 -1
  130. package/app/templates/orders/index/new.js +1 -1
  131. package/app/templates/orders/index/view.js +1 -1
  132. package/app/templates/orders/index.js +1 -1
  133. package/app/templates/products/index/category/edit.js +1 -1
  134. package/app/templates/products/index/category/new.js +1 -1
  135. package/app/templates/products/index/category.js +1 -1
  136. package/app/templates/products/index/index/edit.js +1 -1
  137. package/app/templates/products/index/index.js +1 -1
  138. package/app/templates/products/index.js +1 -1
  139. package/app/templates/settings/api.js +1 -1
  140. package/app/templates/settings/gateways.js +1 -1
  141. package/app/templates/settings/index.js +1 -1
  142. package/app/templates/settings/locations.js +1 -1
  143. package/app/templates/settings/notifications.js +1 -1
  144. package/app/templates/settings.js +1 -1
  145. package/app/utils/create-shareable-link.js +1 -1
  146. package/app/utils/get-gateway-schemas.js +1 -1
  147. package/app/utils/get-notification-schemas.js +1 -1
  148. package/composer.json +88 -0
  149. package/extension.json +10 -0
  150. package/package.json +6 -6
  151. package/phpstan.neon.dist +8 -0
  152. package/phpunit.xml.dist +16 -0
  153. package/server/.gitattributes +14 -0
  154. package/server/README.md +40 -0
  155. package/server/config/api.php +101 -0
  156. package/server/config/database.connections.php +57 -0
  157. package/server/config/storefront.php +19 -0
  158. package/server/config/twilio-notification-channel.php +36 -0
  159. package/server/migrations/2023_05_03_025307_create_carts_table.php +44 -0
  160. package/server/migrations/2023_05_03_025307_create_checkouts_table.php +51 -0
  161. package/server/migrations/2023_05_03_025307_create_gateways_table.php +48 -0
  162. package/server/migrations/2023_05_03_025307_create_network_stores_table.php +36 -0
  163. package/server/migrations/2023_05_03_025307_create_networks_table.php +56 -0
  164. package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +43 -0
  165. package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +44 -0
  166. package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +38 -0
  167. package/server/migrations/2023_05_03_025307_create_product_addons_table.php +43 -0
  168. package/server/migrations/2023_05_03_025307_create_product_hours_table.php +37 -0
  169. package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +34 -0
  170. package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +40 -0
  171. package/server/migrations/2023_05_03_025307_create_product_variants_table.php +44 -0
  172. package/server/migrations/2023_05_03_025307_create_products_table.php +59 -0
  173. package/server/migrations/2023_05_03_025307_create_reviews_table.php +41 -0
  174. package/server/migrations/2023_05_03_025307_create_store_hours_table.php +37 -0
  175. package/server/migrations/2023_05_03_025307_create_store_locations_table.php +38 -0
  176. package/server/migrations/2023_05_03_025307_create_stores_table.php +57 -0
  177. package/server/migrations/2023_05_03_025307_create_votes_table.php +39 -0
  178. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +40 -0
  179. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +48 -0
  180. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +40 -0
  181. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +40 -0
  182. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +42 -0
  183. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +40 -0
  184. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +40 -0
  185. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +38 -0
  186. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +38 -0
  187. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +32 -0
  188. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +34 -0
  189. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +32 -0
  190. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +32 -0
  191. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +44 -0
  192. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +38 -0
  193. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +32 -0
  194. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +40 -0
  195. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +42 -0
  196. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +38 -0
  197. package/server/src/Auth/Schemas/Storefront.php +95 -0
  198. package/server/src/Console/Commands/NotifyStorefrontOrderNearby.php +86 -0
  199. package/server/src/Expansions/EntityExpansion.php +44 -0
  200. package/server/src/Http/Controllers/ActionController.php +119 -0
  201. package/server/src/Http/Controllers/AddonCategoryController.php +13 -0
  202. package/server/src/Http/Controllers/CustomerController.php +13 -0
  203. package/server/src/Http/Controllers/GatewayController.php +13 -0
  204. package/server/src/Http/Controllers/MetricsController.php +71 -0
  205. package/server/src/Http/Controllers/NetworkController.php +170 -0
  206. package/server/src/Http/Controllers/NotificationChannelController.php +13 -0
  207. package/server/src/Http/Controllers/OrderController.php +154 -0
  208. package/server/src/Http/Controllers/ProductAddonCategoryController.php +14 -0
  209. package/server/src/Http/Controllers/ProductAddonController.php +14 -0
  210. package/server/src/Http/Controllers/ProductController.php +123 -0
  211. package/server/src/Http/Controllers/ProductHourController.php +13 -0
  212. package/server/src/Http/Controllers/ProductVariantController.php +13 -0
  213. package/server/src/Http/Controllers/ProductVariantOptionController.php +13 -0
  214. package/server/src/Http/Controllers/ReviewController.php +13 -0
  215. package/server/src/Http/Controllers/StoreController.php +26 -0
  216. package/server/src/Http/Controllers/StoreHourController.php +13 -0
  217. package/server/src/Http/Controllers/StoreLocationController.php +13 -0
  218. package/server/src/Http/Controllers/StorefrontController.php +15 -0
  219. package/server/src/Http/Controllers/VoteController.php +13 -0
  220. package/server/src/Http/Controllers/v1/CartController.php +161 -0
  221. package/server/src/Http/Controllers/v1/CategoryController.php +138 -0
  222. package/server/src/Http/Controllers/v1/CheckoutController.php +957 -0
  223. package/server/src/Http/Controllers/v1/CustomerController.php +482 -0
  224. package/server/src/Http/Controllers/v1/GatewayControllerController.php +11 -0
  225. package/server/src/Http/Controllers/v1/NetworkController.php +281 -0
  226. package/server/src/Http/Controllers/v1/PaymentMethodController.php +11 -0
  227. package/server/src/Http/Controllers/v1/ProductController.php +94 -0
  228. package/server/src/Http/Controllers/v1/ReviewController.php +270 -0
  229. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +402 -0
  230. package/server/src/Http/Controllers/v1/StoreController.php +176 -0
  231. package/server/src/Http/Filter/AddonCategoryFilter.php +19 -0
  232. package/server/src/Http/Filter/CustomerFilter.php +18 -0
  233. package/server/src/Http/Filter/GatewayFilter.php +13 -0
  234. package/server/src/Http/Filter/NetworkFilter.php +18 -0
  235. package/server/src/Http/Filter/NotificationChannelFilter.php +13 -0
  236. package/server/src/Http/Filter/OrderFilter.php +46 -0
  237. package/server/src/Http/Filter/ProductFilter.php +28 -0
  238. package/server/src/Http/Filter/StoreFilter.php +42 -0
  239. package/server/src/Http/Filter/StoreLocationFilter.php +23 -0
  240. package/server/src/Http/Middleware/SetStorefrontSession.php +130 -0
  241. package/server/src/Http/Requests/AddStoreToNetworkCategory.php +45 -0
  242. package/server/src/Http/Requests/CaptureOrderRequest.php +30 -0
  243. package/server/src/Http/Requests/CreateCustomerRequest.php +44 -0
  244. package/server/src/Http/Requests/CreateReviewRequest.php +34 -0
  245. package/server/src/Http/Requests/GetServiceQuoteFromCart.php +40 -0
  246. package/server/src/Http/Requests/InitializeCheckoutRequest.php +38 -0
  247. package/server/src/Http/Requests/NetworkActionRequest.php +43 -0
  248. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +31 -0
  249. package/server/src/Http/Resources/Cart.php +31 -0
  250. package/server/src/Http/Resources/Category.php +48 -0
  251. package/server/src/Http/Resources/Customer.php +36 -0
  252. package/server/src/Http/Resources/Gateway.php +32 -0
  253. package/server/src/Http/Resources/Media.php +29 -0
  254. package/server/src/Http/Resources/Network.php +48 -0
  255. package/server/src/Http/Resources/Product.php +209 -0
  256. package/server/src/Http/Resources/Review.php +45 -0
  257. package/server/src/Http/Resources/ReviewCustomer.php +60 -0
  258. package/server/src/Http/Resources/Store.php +76 -0
  259. package/server/src/Http/Resources/StoreHour.php +29 -0
  260. package/server/src/Http/Resources/StoreLocation.php +33 -0
  261. package/server/src/Imports/ProductsImport.php +20 -0
  262. package/server/src/Jobs/DownloadProductImageUrl.php +60 -0
  263. package/server/src/Listeners/HandleOrderCompleted.php +31 -0
  264. package/server/src/Listeners/HandleOrderDispatched.php +34 -0
  265. package/server/src/Listeners/HandleOrderDriverAssigned.php +37 -0
  266. package/server/src/Listeners/HandleOrderStarted.php +27 -0
  267. package/server/src/Mail/StorefrontNetworkInvite.php +48 -0
  268. package/server/src/Models/AddonCategory.php +30 -0
  269. package/server/src/Models/Cart.php +691 -0
  270. package/server/src/Models/Checkout.php +166 -0
  271. package/server/src/Models/Customer.php +88 -0
  272. package/server/src/Models/Gateway.php +165 -0
  273. package/server/src/Models/Network.php +300 -0
  274. package/server/src/Models/NetworkStore.php +86 -0
  275. package/server/src/Models/NotificationChannel.php +147 -0
  276. package/server/src/Models/PaymentMethod.php +99 -0
  277. package/server/src/Models/Product.php +315 -0
  278. package/server/src/Models/ProductAddon.php +128 -0
  279. package/server/src/Models/ProductAddonCategory.php +90 -0
  280. package/server/src/Models/ProductHour.php +59 -0
  281. package/server/src/Models/ProductStoreLocation.php +77 -0
  282. package/server/src/Models/ProductVariant.php +125 -0
  283. package/server/src/Models/ProductVariantOption.php +86 -0
  284. package/server/src/Models/Review.php +127 -0
  285. package/server/src/Models/Store.php +478 -0
  286. package/server/src/Models/StoreHour.php +59 -0
  287. package/server/src/Models/StoreLocation.php +126 -0
  288. package/server/src/Models/StorefrontModel.php +22 -0
  289. package/server/src/Models/Vote.php +84 -0
  290. package/server/src/Notifications/StorefrontOrderCanceled.php +196 -0
  291. package/server/src/Notifications/StorefrontOrderCompleted.php +201 -0
  292. package/server/src/Notifications/StorefrontOrderCreated.php +157 -0
  293. package/server/src/Notifications/StorefrontOrderDriverAssigned.php +200 -0
  294. package/server/src/Notifications/StorefrontOrderEnroute.php +199 -0
  295. package/server/src/Notifications/StorefrontOrderNearby.php +201 -0
  296. package/server/src/Notifications/StorefrontOrderPreparing.php +202 -0
  297. package/server/src/Notifications/StorefrontOrderReadyForPickup.php +202 -0
  298. package/server/src/Observers/NetworkObserver.php +40 -0
  299. package/server/src/Observers/ProductObserver.php +118 -0
  300. package/server/src/Providers/EventServiceProvider.php +23 -0
  301. package/server/src/Providers/StorefrontServiceProvider.php +103 -0
  302. package/server/src/Support/Metrics.php +193 -0
  303. package/server/src/Support/OrderConfig.php +13 -0
  304. package/server/src/Support/QPay.php +208 -0
  305. package/server/src/Support/Storefront.php +201 -0
  306. package/server/src/routes.php +180 -0
  307. package/server/tests/Feature.php +5 -0
@@ -0,0 +1,281 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Fleetbase\Storefront\Http\Resources\Store as StorefrontStore;
7
+ use Fleetbase\Storefront\Http\Resources\StoreLocation as StorefrontStoreLocation;
8
+ use Fleetbase\Storefront\Models\Store;
9
+ use Fleetbase\Storefront\Models\StoreLocation;
10
+ use Fleetbase\Models\Category;
11
+ use Fleetbase\FleetOps\Support\Utils;
12
+ use Grimzy\LaravelMysqlSpatial\Types\Point;
13
+ use Illuminate\Http\Request;
14
+
15
+ class NetworkController extends Controller
16
+ {
17
+ /**
18
+ * Returns all stores within the network.
19
+ *
20
+ * @param \Illuminate\Http\Request $request
21
+ * @return \Illuminate\Http\Response
22
+ */
23
+ public function stores(Request $request)
24
+ {
25
+ if (session('storefront_store')) {
26
+ return response()->error('Stores cannot have stores!');
27
+ }
28
+
29
+ $sort = $request->input('sort', false);
30
+ $limit = $request->input('limit', false);
31
+ $offset = $request->input('offset', false);
32
+ $ids = $request->input('ids', []);
33
+ $tagged = $request->input('tagged', []);
34
+ $query = $request->input('query', false);
35
+ $location = $request->input('location');
36
+ $maxDistance = $request->input('maximum_distance', null);
37
+ $exclude = $request->input('exclude', []);
38
+
39
+ if (is_string($tagged)) {
40
+ $tagged = explode(',', $tagged);
41
+ }
42
+
43
+ if (is_string($exclude)) {
44
+ $exclude = explode(',', $exclude);
45
+ }
46
+
47
+ if (is_string($ids)) {
48
+ $ids = explode(',', $ids);
49
+ }
50
+
51
+ /** @var \Illuminate\Database\Query\Builder $query */
52
+ $query = Store::select('*')
53
+ ->where('company_uuid', session('company'))
54
+ ->with(['logo', 'backdrop', 'media'])
55
+ ->whereHas('locations')
56
+ ->whereHas('networks', function ($q) use ($request) {
57
+ $q->where('network_uuid', session('storefront_network'));
58
+
59
+ // Query stores without a category
60
+ if ($request->filled('without_category')) {
61
+ $q->whereNull('category_uuid');
62
+ }
63
+
64
+ // Query stores by category
65
+ if ($request->filled('category')) {
66
+ $category = Category::select('uuid')->where('public_id', $request->input('category'))->first();
67
+ $q->where('category_uuid', $category->uuid);
68
+ // $q->whereHas('category', function ())
69
+ }
70
+ });
71
+
72
+ // query stores using tags provided
73
+ if (!empty($tagged)) {
74
+ $query->where(function ($q) use ($tagged) {
75
+ foreach ($tagged as $tag) {
76
+ $q->orWhereJsonContains('tags', $tag);
77
+ }
78
+ });
79
+ }
80
+
81
+ // if we need to exclude specific stores
82
+ if (!empty($exclude)) {
83
+ $query->where(function ($q) use ($exclude) {
84
+ $q->whereNotIn('public_id', $exclude);
85
+ });
86
+ }
87
+
88
+ // if query for specific ids
89
+ if (!empty($ids)) {
90
+ $query->where(function ($q) use ($ids) {
91
+ $q->whereIn('public_id', $ids);
92
+ });
93
+ }
94
+
95
+ // only get stores with `StoreLocation` within $maxDistance (meters)
96
+ // need to move place->location as alias column to storeLocation->location
97
+ // when place gets updated/changed it also updates corresponding storeLocation in observer
98
+ // if ($location && $maxDistance) {
99
+ // $query->whereHas('locations', function ($q) use ($maxDistance) {
100
+ // });
101
+ // }
102
+
103
+ switch ($sort) {
104
+ case 'highest_rated':
105
+ $query->withAvg('reviews', 'rating')->orderByDesc('reviews_avg_rating');
106
+ case 'lowest_rated':
107
+ $query->withAvg('reviews', 'rating')->orderBy('reviews_avg_rating');
108
+ case 'newest':
109
+ $query->orderByDesc('created_at');
110
+ case 'oldest':
111
+ $query->orderBy('created_at');
112
+ case 'popular':
113
+ $query->withCount('checkouts')->orderByDesc('checkouts_count');
114
+ }
115
+
116
+ if ($limit) {
117
+ $query->limit($limit);
118
+ }
119
+
120
+ if ($offset) {
121
+ $query->offset($offset);
122
+ }
123
+
124
+ $stores = $query->get();
125
+
126
+ // handle nearest sort special case due to location depth
127
+ if ($sort === 'nearest' && $location) {
128
+ $coordinates = Utils::getPointFromCoordinates($location);
129
+
130
+ $stores = $stores->sort(function ($storeA, $storeB) use ($coordinates) {
131
+ $distanceA = $storeA->locations->sort(function ($locationA, $locationB) use ($coordinates) {
132
+ $distanceA = Utils::vincentyGreatCircleDistance($coordinates, $locationA->place->location);
133
+ $distanceB = Utils::vincentyGreatCircleDistance($coordinates, $locationB->place->location);
134
+
135
+ return $distanceA - $distanceB;
136
+ })->map(function ($location) use ($coordinates) {
137
+ $location->distance = Utils::vincentyGreatCircleDistance($coordinates, $location->place->location);
138
+ return $location;
139
+ })->first()->distance;
140
+
141
+ $distanceB = $storeB->locations->sort(function ($locationA, $locationB) use ($coordinates) {
142
+ $distanceA = Utils::vincentyGreatCircleDistance($coordinates, $locationA->place->location);
143
+ $distanceB = Utils::vincentyGreatCircleDistance($coordinates, $locationB->place->location);
144
+
145
+ return $distanceA - $distanceB;
146
+ })->map(function ($location) use ($coordinates) {
147
+ $location->distance = Utils::vincentyGreatCircleDistance($coordinates, $location->place->location);
148
+ return $location;
149
+ })->first()->distance;
150
+
151
+ return $distanceA - $distanceB;
152
+ });
153
+ }
154
+
155
+ // sort trending ( most checkouts within 24h )
156
+ if ($sort === 'trending') {
157
+ $stores = $stores->sortByDesc('24h_checkouts_count');
158
+ }
159
+
160
+ return StorefrontStore::collection($stores);
161
+ }
162
+
163
+ /**
164
+ * Returns all store locations within the network.
165
+ *
166
+ * @param \Illuminate\Http\Request $request
167
+ * @return \Illuminate\Http\Response
168
+ */
169
+ public function storeLocations(Request $request)
170
+ {
171
+ $limit = $request->input('limit', 30);
172
+ $ids = $request->input('ids', []);
173
+ $exclude = $request->input('exclude', []);
174
+ $offset = $request->input('offset');
175
+ $location = $request->input('location');
176
+ $tagged = $request->input('tagged', []);
177
+ $searchQuery = $request->input('query', false);
178
+ $shouldIncludeStore = $request->input('with_store', false);
179
+ $coordinates = Utils::getPointFromCoordinates($location);
180
+
181
+ if (is_string($tagged)) {
182
+ $tagged = explode(',', $tagged);
183
+ }
184
+
185
+ if (is_string($exclude)) {
186
+ $exclude = explode(',', $exclude);
187
+ }
188
+
189
+ if (is_string($ids)) {
190
+ $ids = explode(',', $ids);
191
+ }
192
+
193
+ $databaseName = config('database.connections.mysql.database');
194
+ $placesTableName = $databaseName . '.places';
195
+
196
+ $query = StoreLocation::select(['store_locations.*', $placesTableName . '.location', $placesTableName . '.uuid'])
197
+ ->join($placesTableName, $placesTableName . '.uuid', '=', 'store_locations.place_uuid')
198
+ ->whereHas('store', function ($q) use ($tagged, $searchQuery) {
199
+ $q->whereHas('networks', function ($q) {
200
+ $q->where('network_uuid', session('storefront_network'));
201
+ });
202
+
203
+ if (!empty($tagged)) {
204
+ $q->where(function ($q) use ($tagged) {
205
+ foreach ($tagged as $tag) {
206
+ $q->orWhereJsonContains('tags', $tag);
207
+ }
208
+ });
209
+ }
210
+
211
+ if ($searchQuery) {
212
+ $q->search($searchQuery);
213
+ }
214
+ });
215
+
216
+ if ($shouldIncludeStore) {
217
+ $query->with('store');
218
+ }
219
+
220
+ // if we need to exclude specific stores
221
+ if (!empty($exclude)) {
222
+ $query->where(function ($q) use ($exclude) {
223
+ $q->whereNotIn('store_locations.public_id', $exclude);
224
+ });
225
+ }
226
+
227
+ // if query for specific ids
228
+ if (!empty($ids)) {
229
+ $query->where(function ($q) use ($ids) {
230
+ $q->whereIn('store_locations.public_id', $ids);
231
+ });
232
+ }
233
+
234
+ if ($limit) {
235
+ $query->limit($limit);
236
+ }
237
+
238
+ if ($offset) {
239
+ $query->offset($offset);
240
+ }
241
+
242
+ if ($coordinates instanceof Point) {
243
+ $query->orderByDistance('location', $coordinates);
244
+ }
245
+
246
+ $locations = $query->get();
247
+
248
+ return StorefrontStoreLocation::collection($locations);
249
+ }
250
+
251
+ /**
252
+ * Returns all tags from stores/categories throughout the network
253
+ *
254
+ * @param \Illuminate\Http\Request $request
255
+ * @return \Illuminate\Http\Response
256
+ */
257
+ public function tags(Request $request)
258
+ {
259
+ $tags = [];
260
+
261
+ $stores = Store::select(['tags'])
262
+ ->whereHas('locations')
263
+ ->whereHas('networks', function ($q) {
264
+ $q->where('network_uuid', session('storefront_network'));
265
+ })->get();
266
+
267
+ foreach ($stores as $store) {
268
+ $tags = array_merge($tags, $store->tags ?? []);
269
+ }
270
+
271
+ // $categories = Category::select(['tags'])->where(['owner_uuid' => session('storefront_network'), 'for' => 'storefront_network']);
272
+ // foreach ($categories as $category) {
273
+ // $tags = array_merge($tags, $category->tags ?? []);
274
+ // }
275
+
276
+ // unique only
277
+ $tags = array_values(array_unique($tags));
278
+
279
+ return response()->json($tags);
280
+ }
281
+ }
@@ -0,0 +1,11 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Illuminate\Http\Request;
7
+
8
+ class PaymentMethodController extends Controller
9
+ {
10
+ //
11
+ }
@@ -0,0 +1,94 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Fleetbase\Storefront\Http\Resources\Product as StorefrontProduct;
7
+ use Fleetbase\Http\Resources\v1\DeletedResource;
8
+ use Fleetbase\Models\Category;
9
+ use Fleetbase\Storefront\Models\Product;
10
+ use Illuminate\Database\Eloquent\ModelNotFoundException;
11
+ use Illuminate\Http\Request;
12
+
13
+ class ProductController extends Controller
14
+ {
15
+ /**
16
+ * Query for Storefront Product resources.
17
+ *
18
+ * @param \Illuminate\Http\Request $request
19
+ * @return \Fleetbase\Http\Resources\DriverCollection
20
+ */
21
+ public function query(Request $request)
22
+ {
23
+ $results = Product::queryWithRequest($request, function (&$query, $request) {
24
+ // for stores
25
+ if (session('storefront_store')) {
26
+ $query->where(['store_uuid' => session('storefront_store')]);
27
+ }
28
+
29
+ // for networks
30
+ if (session('storefront_network')) {
31
+ $query->whereHas('store', function ($sq) {
32
+ $sq->whereHas('networks', function ($nq) {
33
+ $nq->where('network_uuid', session('storefront_network'));
34
+ });
35
+ });
36
+ }
37
+
38
+ // @todo When done dev is completed make sure status is published - also add status field to product view
39
+ $query->where('is_available', 1);
40
+ $query->with(['addonCategories.category', 'variants.options', 'files']);
41
+
42
+ if ($request->filled('category')) {
43
+ $category = Category::where(['public_id' => $request->input('category'), 'for' => 'storefront_product'])->first();
44
+
45
+ if ($category) {
46
+ $query->where('category_uuid', $category->uuid);
47
+ }
48
+ }
49
+ });
50
+
51
+ return StorefrontProduct::collection($results);
52
+ }
53
+
54
+ /**
55
+ * Finds a single Storefront Product resources.
56
+ *
57
+ * @param \Illuminate\Http\Request $request
58
+ * @return \Fleetbase\Http\Resources\EntityCollection
59
+ */
60
+ public function find($id)
61
+ {
62
+ // find for the product
63
+ try {
64
+ $product = Product::findRecordOrFail($id);
65
+ } catch (ModelNotFoundException $exception) {
66
+ return response()->error('Product resource not found.');
67
+ }
68
+
69
+ // response the product resource
70
+ return new StorefrontProduct($product);
71
+ }
72
+
73
+ /**
74
+ * Deletes a Storefront Product resources.
75
+ *
76
+ * @param \Illuminate\Http\Request $request
77
+ * @return \Fleetbase\Http\Resources\v1\DeletedResource
78
+ */
79
+ public function delete($id)
80
+ {
81
+ // find for the product
82
+ try {
83
+ $product = Product::findRecordOrFail($id);
84
+ } catch (ModelNotFoundException $exception) {
85
+ return response()->error('Product resource not found.');
86
+ }
87
+
88
+ // delete the product
89
+ $product->delete();
90
+
91
+ // response the product resource
92
+ return new DeletedResource($product);
93
+ }
94
+ }
@@ -0,0 +1,270 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Fleetbase\Storefront\Http\Requests\CreateReviewRequest;
7
+ use Fleetbase\Storefront\Http\Resources\Review as StorefrontReview;
8
+ use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
9
+ use Fleetbase\Storefront\Models\Store;
10
+ use Fleetbase\Storefront\Models\Review;
11
+ use Fleetbase\Storefront\Support\Storefront;
12
+ use Fleetbase\FleetOps\Support\Utils;
13
+ use Fleetbase\Models\File;
14
+ use Illuminate\Database\Eloquent\ModelNotFoundException;
15
+ use Illuminate\Http\Request;
16
+ use Illuminate\Support\Facades\Storage;
17
+
18
+ class ReviewController extends Controller
19
+ {
20
+ /**
21
+ * Query for Storefront Review resources.
22
+ *
23
+ * @param \Illuminate\Http\Request $request
24
+ * @return \Illuminate\Http\Response
25
+ */
26
+ public function query(Request $request)
27
+ {
28
+ $limit = $request->input('limit', false);
29
+ $offset = $request->input('offset', false);
30
+ $sort = $request->input('sort');
31
+
32
+ if (session('storefront_store')) {
33
+ $results = Review::queryWithRequest($request, function (&$query) use ($limit, $offset, $sort) {
34
+ $query->where('subject_uuid', session('storefront_store'));
35
+
36
+ if ($limit) {
37
+ $query->limit($limit);
38
+ }
39
+
40
+ if ($offset) {
41
+ $query->limit($offset);
42
+ }
43
+
44
+ if ($sort) {
45
+ switch ($sort) {
46
+ case 'highest':
47
+ case 'highest rated':
48
+ $query->orderByDesc('rating');
49
+ break;
50
+
51
+ case 'lowest':
52
+ case 'lowest rated':
53
+ $query->orderBy('rating');
54
+ break;
55
+
56
+ case 'newest':
57
+ case 'newest first':
58
+ $query->orderByDesc('created_at');
59
+ break;
60
+
61
+ case 'oldest':
62
+ case 'oldest first':
63
+ $query->orderBy('created_at');
64
+ break;
65
+ }
66
+ }
67
+ });
68
+ }
69
+
70
+ if (session('storefront_network')) {
71
+ if ($request->filled('store')) {
72
+ $store = Store::where([
73
+ 'company_uuid' => session('company'),
74
+ 'public_id' => $request->input('store')
75
+ ])->whereHas('networks', function ($q) {
76
+ $q->where('network_uuid', session('storefront_network'));
77
+ })->first();
78
+
79
+ if (!$store) {
80
+ return response()->json(['error' => 'Cannot find reviews for store'], 400);
81
+ }
82
+
83
+ $results = Review::queryWithRequest($request, function (&$query) use ($store, $sort, $limit, $offset) {
84
+ $query->where('subject_uuid', $store->uuid);
85
+
86
+ if ($limit) {
87
+ $query->limit($limit);
88
+ }
89
+
90
+ if ($offset) {
91
+ $query->limit($offset);
92
+ }
93
+
94
+ if ($sort) {
95
+ switch ($sort) {
96
+ case 'highest':
97
+ case 'highest rated':
98
+ $query->orderByDesc('rating');
99
+ break;
100
+
101
+ case 'lowest':
102
+ case 'lowest rated':
103
+ $query->orderBy('rating');
104
+ break;
105
+
106
+ case 'newest':
107
+ case 'newest first':
108
+ $query->orderByDesc('created_at');
109
+ break;
110
+
111
+ case 'oldest':
112
+ case 'oldest first':
113
+ $query->orderBy('created_at');
114
+ break;
115
+ }
116
+ }
117
+ });
118
+ }
119
+ }
120
+
121
+ return StorefrontReview::collection($results);
122
+ }
123
+
124
+ /**
125
+ * Coutns the number of ratings between 1-5 for a store.
126
+ *
127
+ * @param \Illuminate\Http\Request $request
128
+ * @return \Illuminate\Http\Response
129
+ */
130
+ public function count(Request $request)
131
+ {
132
+ $counts = [];
133
+ $range = range(1, 5);
134
+
135
+ if (session('storefront_store')) {
136
+ foreach ($range as $rating) {
137
+ $counts[$rating] = Review::where(['subject_uuid' => session('storefront_store'), 'rating' => $rating])->count();
138
+ }
139
+ }
140
+
141
+ if (session('storefront_network')) {
142
+ if ($request->filled('store')) {
143
+ $store = Store::where([
144
+ 'company_uuid' => session('company'),
145
+ 'public_id' => $request->input('store')
146
+ ])->whereHas('networks', function ($q) {
147
+ $q->where('network_uuid', session('storefront_network'));
148
+ })->first();
149
+
150
+ if (!$store) {
151
+ return response()->json(['error' => 'Cannot count reviews for store'], 400);
152
+ }
153
+
154
+ foreach ($range as $rating) {
155
+ $counts[$rating] = Review::where(['subject_uuid' => $store->uuid, 'rating' => $rating])->count();
156
+ }
157
+ }
158
+ }
159
+
160
+ return response()->json($counts);
161
+ }
162
+
163
+ /**
164
+ * Finds a single Storefront Review resources.
165
+ *
166
+ * @param string $id
167
+ * @return \Fleetbase\Http\Response
168
+ */
169
+ public function find($id)
170
+ {
171
+ // find for the review
172
+ try {
173
+ $review = Review::findRecordOrFail($id);
174
+ } catch (ModelNotFoundException $exception) {
175
+ return response()->error('Review resource not found.');
176
+ }
177
+
178
+ // response the review resource
179
+ return new StorefrontReview($review);
180
+ }
181
+
182
+ /**
183
+ * Create a review.
184
+ *
185
+ * @param \Fleetbase\Storefront\Http\Requests\CreateReviewRequest $request
186
+ * @return \Fleetbase\Http\Response
187
+ */
188
+ public function create(CreateReviewRequest $request)
189
+ {
190
+ $customer = Storefront::getCustomerFromToken();
191
+ $about = Storefront::about();
192
+
193
+ if (!$customer) {
194
+ return response()->error('Not authorized to create reviews');
195
+ }
196
+
197
+ $subject = Utils::resolveSubject($request->input('subject'));
198
+
199
+ if (!$subject) {
200
+ return response()->error('Invalid subject for review');
201
+ }
202
+
203
+ $review = Review::create([
204
+ 'created_by_uuid' => $customer->user_uuid,
205
+ 'customer_uuid' => $customer->uuid,
206
+ 'subject_uuid' => $subject->uuid,
207
+ 'subject_type' => Utils::getMutationType($subject),
208
+ 'rating' => $request->input('rating'),
209
+ 'content' => $request->input('content')
210
+ ]);
211
+
212
+ // if files provided
213
+ if ($request->filled('files')) {
214
+ $files = $request->input('files');
215
+
216
+ foreach ($files as $upload) {
217
+
218
+ $data = Utils::get($upload, 'data');
219
+ $mimeType = Utils::get($upload, 'type');
220
+ $extension = File::getExtensionFromMimeType($mimeType);
221
+ $bucketPath = 'hyperstore/' . $about->public_id . '/review-photos/' . $review->uuid . '/' . File::randomFileName($extension);
222
+
223
+ // upload file to path
224
+ $upload = Storage::disk('s3')->put($bucketPath, base64_decode($data), 'public');
225
+
226
+ // create the file
227
+ $file = File::create([
228
+ 'company_uuid' => session('company'),
229
+ 'uploader_uuid' => $customer->user_uuid,
230
+ 'subject_uuid' => $review->uuid,
231
+ 'subject_type' => Utils::getMutationType($review),
232
+ 'name' => basename($bucketPath),
233
+ 'original_filename' => basename($bucketPath),
234
+ 'extension' => $extension,
235
+ 'content_type' => $mimeType,
236
+ 'path' => $bucketPath,
237
+ 'bucket' => config('filesystems.disks.s3.bucket'),
238
+ 'type' => 'storefront_review_upload',
239
+ 'size' => Utils::getBase64ImageSize($data)
240
+ ]);
241
+
242
+ $review->files->push($file);
243
+ }
244
+ }
245
+
246
+ return new StorefrontReview($review);
247
+ }
248
+
249
+ /**
250
+ * Deletes a Storefront Review resources.
251
+ *
252
+ * @param \Illuminate\Http\Request $request
253
+ * @return \Fleetbase\Http\Resources\v1\DeletedResource
254
+ */
255
+ public function delete($id)
256
+ {
257
+ // find for the product
258
+ try {
259
+ $review = Review::findRecordOrFail($id);
260
+ } catch (ModelNotFoundException $exception) {
261
+ return response()->error('Review resource not found.');
262
+ }
263
+
264
+ // delete the review
265
+ $review->delete();
266
+
267
+ // response the review resource
268
+ return new DeletedResource($review);
269
+ }
270
+ }