@fleetbase/storefront-engine 0.3.18 → 0.3.21

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 (81) hide show
  1. package/addon/components/customer-panel/orders.hbs +107 -104
  2. package/addon/components/customer-panel/orders.js +52 -45
  3. package/addon/components/modals/incoming-order.hbs +208 -199
  4. package/addon/components/modals/order-ready-assign-driver.hbs +1 -1
  5. package/addon/components/order-panel/details.js +0 -2
  6. package/addon/components/order-panel.hbs +314 -1
  7. package/addon/components/order-panel.js +51 -3
  8. package/addon/components/widget/customers.hbs +75 -51
  9. package/addon/components/widget/customers.js +29 -41
  10. package/addon/components/widget/orders.hbs +278 -119
  11. package/addon/components/widget/orders.js +75 -80
  12. package/addon/components/widget/storefront-metrics.hbs +3 -6
  13. package/addon/components/widget/storefront-metrics.js +25 -41
  14. package/addon/controllers/orders/index.js +214 -105
  15. package/addon/controllers/settings/gateways.js +1 -1
  16. package/addon/helpers/get-tip-amount.js +13 -2
  17. package/addon/routes/application.js +2 -4
  18. package/addon/services/order-actions.js +248 -0
  19. package/addon/services/storefront.js +2 -0
  20. package/addon/styles/storefront-engine.css +48 -0
  21. package/addon/templates/home.hbs +2 -1
  22. package/addon/templates/orders/index/view.hbs +1 -1
  23. package/addon/templates/orders/index.hbs +26 -3
  24. package/addon/templates/products/index/index.hbs +28 -28
  25. package/addon/templates/settings.hbs +1 -1
  26. package/app/services/order-actions.js +1 -0
  27. package/composer.json +1 -1
  28. package/extension.json +1 -1
  29. package/package.json +1 -1
  30. package/server/config/storefront.php +20 -0
  31. package/server/migrations/2023_05_03_025307_create_carts_table.php +1 -1
  32. package/server/migrations/2023_05_03_025307_create_checkouts_table.php +1 -1
  33. package/server/migrations/2023_05_03_025307_create_gateways_table.php +1 -1
  34. package/server/migrations/2023_05_03_025307_create_network_stores_table.php +1 -1
  35. package/server/migrations/2023_05_03_025307_create_networks_table.php +1 -1
  36. package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +1 -1
  37. package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +1 -1
  38. package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +1 -1
  39. package/server/migrations/2023_05_03_025307_create_product_addons_table.php +1 -1
  40. package/server/migrations/2023_05_03_025307_create_product_hours_table.php +1 -1
  41. package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +1 -1
  42. package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +1 -1
  43. package/server/migrations/2023_05_03_025307_create_product_variants_table.php +1 -1
  44. package/server/migrations/2023_05_03_025307_create_products_table.php +1 -1
  45. package/server/migrations/2023_05_03_025307_create_reviews_table.php +1 -1
  46. package/server/migrations/2023_05_03_025307_create_store_hours_table.php +1 -1
  47. package/server/migrations/2023_05_03_025307_create_store_locations_table.php +1 -1
  48. package/server/migrations/2023_05_03_025307_create_stores_table.php +1 -1
  49. package/server/migrations/2023_05_03_025307_create_votes_table.php +1 -1
  50. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +1 -1
  51. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +1 -1
  52. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +1 -1
  53. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +1 -1
  54. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +1 -1
  55. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +1 -1
  56. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +1 -1
  57. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +1 -1
  58. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +1 -1
  59. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +1 -1
  60. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +1 -1
  61. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +1 -1
  62. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +1 -1
  63. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +1 -1
  64. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +1 -1
  65. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +1 -1
  66. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +1 -1
  67. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +1 -1
  68. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +1 -1
  69. package/server/src/Http/Controllers/ActionController.php +2 -1
  70. package/server/src/Http/Controllers/OrderController.php +15 -2
  71. package/server/src/Http/Controllers/v1/CheckoutController.php +12 -12
  72. package/server/src/Http/Controllers/v1/CustomerController.php +15 -7
  73. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +5 -5
  74. package/server/src/Http/Requests/CreateCustomerRequest.php +4 -0
  75. package/server/src/Http/Requests/InitializeCheckoutRequest.php +2 -1
  76. package/server/src/Http/Resources/Cart.php +1 -1
  77. package/server/src/Models/Store.php +2 -2
  78. package/server/src/Observers/OrderObserver.php +7 -1
  79. package/server/src/Rules/IsValidLocation.php +2 -2
  80. package/server/src/Support/Storefront.php +34 -0
  81. package/translations/en-us.yaml +6 -1
@@ -1,58 +1,103 @@
1
- <ContentPanel
2
- @title={{this.title}}
3
- @titleStatusRight={{this.orders.length}}
4
- @titleStatuRightClass="info-status-badge"
5
- @hideStatusDot={{true}}
6
- @open={{this.orders.length}}
7
- @pad={{false}}
8
- @wrapperClass={{@wrapperClass}}
9
- @onInsert={{this.setupWidget}}
10
- >
11
- {{#if this.isLoading}}
12
- <div class="px-3 py-2">
13
- <Spinner class="text-sky-400" />
1
+ {{#if (and this.loadOrders.isRunning (not this.loaded))}}
2
+ <div class="next-content-panel-wrapper">
3
+ <div class="next-content-panel-container">
4
+ <div class="next-content-panel next-content-panel-is-closed">
5
+ <div class="next-content-panel-header next-content-panel-toggle next-content-panel-is-closed">
6
+ <a href="javascript:;" class="next-content-panel-header-left">
7
+ <span class="icon-container">
8
+ <Spinner class="text-sky-400" @height="13" @width="13" />
9
+ </span>
10
+ <div class="next-content-panel-title-container">
11
+ <div class="panel-title flex-shrink-0">
12
+ <div class="flex flex-col">
13
+ <span>{{this.title}}</span>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ </a>
18
+ </div>
19
+ </div>
14
20
  </div>
15
- {{/if}}
16
- <div class="hidden md:flex table-wrapper table-fluid">
17
- <table>
18
- <thead>
19
- <tr class="h-12 text-left py-1">
20
- <th>{{t "storefront.component.widget.orders.id-column"}}</th>
21
- <th>{{t "storefront.common.amount"}}</th>
22
- <th>{{t "storefront.common.customer"}}</th>
23
- <th>{{t "storefront.common.driver"}}</th>
24
- <th>{{t "storefront.common.created"}}</th>
25
- <th>{{t "storefront.common.status"}}</th>
26
- <th></th>
27
- </tr>
28
- </thead>
29
- <tbody>
21
+ </div>
22
+ {{else}}
23
+ <ContentPanel
24
+ @title={{this.title}}
25
+ @titleStatusRight={{this.orders.length}}
26
+ @titleStatuRightClass="info-status-badge"
27
+ @hideStatusDot={{true}}
28
+ @open={{gt this.orders.length 0}}
29
+ @isLoading={{this.loadOrders.isRunning}}
30
+ @pad={{false}}
31
+ @wrapperClass={{@wrapperClass}}
32
+ >
33
+ {{#if (media "isMobile")}}
34
+ <div class="flex flex-col p-3 space-y-3">
30
35
  {{#each this.orders as |order|}}
31
- <tr class="h-12">
32
- <td><a href="javascript:;" {{on "click" (fn this.viewOrder order)}}>{{order.public_id}}</a></td>
33
- <td>{{format-currency order.meta.total order.meta.currency}}</td>
34
- <td>{{n-a order.customer.name}}</td>
35
- <td>{{n-a order.driver_assigned.name}}</td>
36
- <td>{{order.createdAgo}}</td>
37
- <td>
38
- <Badge @status={{order.status}} />
39
- </td>
40
- <td>
36
+ <div class="py-2 px-3 rounded-md border border-gray-400 dark:border-gray-700">
37
+ <div class="flex flex-row mb-2">
38
+ <div class="flex-1">
39
+ <a href="javascript:;" {{on "click" (fn this.viewOrder order)}} class="text-sm font-semibold">{{order.public_id}}</a>
40
+ <div class="text-xs">{{order.createdAt}}</div>
41
+ <div class="text-xs">{{order.createdAgo}}</div>
42
+ </div>
43
+ <div class="flex-shrink-0 flex flex-col text-right">
44
+ <Badge class="mb-1" @status={{order.status}} />
45
+ {{#if order.meta.is_pickup}}
46
+ <Badge class="mb-1" @hideStatusDot={{true}} @status="info"><FaIcon @icon="hand-holding-dollar" class="mr-1" />{{t
47
+ "storefront.component.widget.orders.pickup-order"
48
+ }}</Badge>
49
+ {{/if}}
50
+ <div class="text-sm">{{format-currency order.meta.total order.meta.currency}}</div>
51
+ </div>
52
+ </div>
53
+ <div class="flex flex-col space-y-2 flex-wrap mb-2">
54
+ <Button
55
+ @wrapperClass="w-full"
56
+ class="btn-block w-full"
57
+ @size="xs"
58
+ @type="primary"
59
+ @icon="eye"
60
+ @text={{t "storefront.common.view"}}
61
+ @onClick={{fn this.viewOrder order}}
62
+ />
63
+ {{#unless order.meta.is_pickup}}
64
+ <Button
65
+ @wrapperClass="w-full"
66
+ class="btn-block w-full"
67
+ @size="xs"
68
+ @type="default"
69
+ @icon="id-card"
70
+ @text={{if order.has_driver_assigned (t "storefront.component.widget.orders.change-driver") (t "storefront.component.widget.orders.assign-driver")}}
71
+ @onClick={{fn this.assignDriver order}}
72
+ />
73
+ {{/unless}}
41
74
  {{#if order.isFresh}}
42
75
  <Button
76
+ @wrapperClass="w-full"
77
+ class="btn-block w-full"
43
78
  @size="xs"
44
79
  @type="success"
45
80
  @iconPrefix="fas"
46
81
  @icon="check"
47
- @text={{concat (t "storefront.component.widget.orders.accept-order") "!"}}
82
+ @text={{t "storefront.component.widget.orders.accept-order"}}
48
83
  @onClick={{fn this.acceptOrder order}}
49
84
  />
50
85
  {{/if}}
51
86
  {{#if order.isPreparing}}
52
- <Button @size="xs" @type="success" @icon="bells" @text={{t "storefront.component.widget.orders.mark-as-ready"}} @onClick={{fn this.markAsReady order}} />
87
+ <Button
88
+ @wrapperClass="w-full"
89
+ class="btn-block w-full"
90
+ @size="xs"
91
+ @type="success"
92
+ @icon="bell-concierge"
93
+ @text={{t "storefront.component.widget.orders.mark-as-ready"}}
94
+ @onClick={{fn this.markAsReady order}}
95
+ />
53
96
  {{/if}}
54
97
  {{#if order.isPickupReady}}
55
98
  <Button
99
+ @wrapperClass="w-full"
100
+ class="btn-block w-full"
56
101
  @size="xs"
57
102
  @type="success"
58
103
  @icon="check"
@@ -60,87 +105,201 @@
60
105
  @onClick={{fn this.markAsCompleted order}}
61
106
  />
62
107
  {{/if}}
63
- </td>
64
- </tr>
65
- {{/each}}
66
- </tbody>
67
- </table>
68
- </div>
69
- <div class="flex flex-col md:hidden p-3 space-y-3">
70
- {{#each this.orders as |order|}}
71
- <div class="py-2 px-3 rounded-md border border-gray-400 dark:border-gray-700">
72
- <div class="flex flex-row mb-3">
73
- <div class="flex-1">
74
- <a href="javascript:;" {{on "click" (fn this.viewOrder order)}} class="font-semibold">{{order.public_id}}</a>
75
- <div>{{order.createdAt}}</div>
76
- <div>{{order.createdAgo}}</div>
77
- </div>
78
- <div class="flex-shrink-0 flex flex-col text-right">
79
- <Badge class="mb-1" @status={{order.status}} />
80
- <div>{{format-currency order.meta.total order.meta.currency}}</div>
81
- </div>
82
- </div>
83
- <div class="flex flex-row space-x-2 flex-wrap">
84
- <Button @size="xs" @type="primary" @icon="eye" @text={{t "storefront.common.view"}} @onClick={{fn this.viewOrder order}} />
85
- <Button
86
- @size="xs"
87
- @type="default"
88
- @icon="steering-wheel"
89
- @text={{if order.has_driver_assigned (t "storefront.component.widget.order.change-driver") (t "storefront.component.widget.order.assign-driver")}}
90
- @onClick={{fn this.assignDriver order}}
91
- />
92
- {{#if order.isFresh}}
93
- <Button
94
- @size="xs"
95
- @type="success"
96
- @iconPrefix="fas"
97
- @icon="check"
98
- @text={{concat (t "storefront.component.widget.orders.accept-order") "!"}}
99
- @onClick={{fn this.acceptOrder order}}
100
- />
101
- {{/if}}
102
- {{#if order.isPreparing}}
103
- <Button @size="xs" @type="success" @icon="bells" @text={{t "storefront.component.widget.orders.mark-as-mark"}} @onClick={{fn this.markAsReady order}} />
104
- {{/if}}
105
- {{#if order.isPickupReady}}
106
- <Button @size="xs" @type="success" @icon="check" @text={{t "storefront.component.widget.orders.mark-as-completed"}} @onClick={{fn this.markAsCompleted order}} />
107
- {{/if}}
108
- </div>
109
- <ContentPanel @title={{t "storefront.component.widget.orders.title"}} class="mt-2">
110
- <div class="flex flex-col py-2 border-t dark:border-gray-800">
111
- <div>{{t "storefront.component.widget.orders.customer"}}: {{n-a order.customer_name}}</div>
112
- <div>{{t "storefront.component.widget.orders.driver"}}: {{n-a order.driver_name}}</div>
113
- </div>
114
- <div class="py-2 space-y-2 border-t dark:border-gray-800">
115
- <div class="flex items-center justify-between">
116
- <span class="dark:text-gray-50">{{t "storefront.component.widget.orders.subtotal"}}</span>
117
- <span class="dark:text-gray-50">{{format-currency order.meta.subtotal order.meta.currency}}</span>
108
+ {{#unless order.isCanceled}}
109
+ <Button
110
+ @wrapperClass="w-full"
111
+ class="btn-block w-full"
112
+ @size="xs"
113
+ @type="danger"
114
+ @icon="ban"
115
+ @text={{t "storefront.component.widget.orders.cancel-order"}}
116
+ @onClick={{fn this.cancelOrder order}}
117
+ />
118
+ {{/unless}}
118
119
  </div>
119
- {{#unless order.meta.is_pickup}}
120
- <div class="flex items-center justify-between">
121
- <span class="dark:text-gray-50">{{t "storefront.component.widget.orders.delivery-fee"}}</span>
122
- <span class="dark:text-gray-50">{{format-currency order.meta.delivery_fee order.meta.currency}}</span>
120
+ <ContentPanel @title={{t "storefront.component.widget.orders.more-details"}}>
121
+ <div class="p-2 space-y-2">
122
+ {{#unless order.meta.is_pickup}}
123
+ <div class="bg-gray-100 border border-gray-200 dark:bg-gray-800 dark:border-gray-700 rounded-md px-4 py-2 space-y-2 h-full">
124
+ <h5 class="dark:text-gray-100 font-semibold">{{t "storefront.component.modals.incoming-order.assigned"}}</h5>
125
+ <div class="flex flex-col space-y-4">
126
+ {{#if order.driver_assigned.id}}
127
+ <div class="flex items-center">
128
+ <Image src={{order.driver_assigned.photoUrl}} class="w-12 h-12 rounded-md shadow-sm mr-4" alt={{order.driver_assigned.name}} />
129
+ <div>
130
+ <h5 class="font-semibold dark:text-white text-xs">{{n-a order.driver_assigned.displayName}}</h5>
131
+ <div class="font-semibold dark:text-gray-100 text-xs">{{n-a
132
+ order.driver_assigned.phone
133
+ (t "storefront.component.modals.incoming-order.no-phone")
134
+ }}</div>
135
+ </div>
136
+ </div>
137
+ {{else}}
138
+ <div>
139
+ <h5 class="text-red-500 text-sm">{{t "storefront.component.modals.incoming-order.not-assigned"}}</h5>
140
+ </div>
141
+ {{/if}}
142
+ <div>
143
+ <Button
144
+ @size="xs"
145
+ @type="default"
146
+ @icon="id-card"
147
+ @text={{if
148
+ order.has_driver_assigned
149
+ (t "storefront.component.modals.incoming-order.change-driver")
150
+ (t "storefront.component.modals.incoming-order.assign-driver")
151
+ }}
152
+ @onClick={{this.assignDriver order}}
153
+ />
154
+ </div>
155
+ </div>
156
+ </div>
157
+ {{/unless}}
158
+ <div class="bg-gray-100 border border-gray-200 dark:bg-gray-800 dark:border-gray-700 rounded-md px-4 py-2 space-y-2 h-full">
159
+ <h5 class="dark:text-gray-100 font-semibold">{{t "storefront.common.customer"}}</h5>
160
+ <div class="">
161
+ <div class="flex flex-row">
162
+ <div>
163
+ <Image src={{avatar-url order.customer.photo_url}} class="w-12 h-12 rounded-md shadow-sm mr-4" alt={{order.customer.name}} />
164
+ </div>
165
+ <div>
166
+ <div class="text-xs font-bold dark:text-gray-100">{{order.customer.name}}</div>
167
+ <div class="text-xs dark:text-gray-100">{{order.customer.email}}</div>
168
+ <div class="text-xs dark:text-gray-100">{{order.customer.phone}}</div>
169
+ </div>
170
+ </div>
171
+ {{#unless order.meta.is_pickup}}
172
+ <div class="mt-2">
173
+ <h5 class="dark:text-gray-100 font-semibold text-xs truncate">{{t "storefront.component.modals.incoming-order.address"}}</h5>
174
+ <div class="flex flex-row mt-1">
175
+ <div class="flex items-center justify-center rounded-full bg-blue-500 w-8 h-8 mr-3">
176
+ <FaIcon @icon="map-marker-alt" class="text-white" />
177
+ </div>
178
+ <div class="truncate">
179
+ <DisplayPlace @place={{order.payload.dropoff}} @type="dropoff" @addressClass="text-xs dark:text-gray-100" @noAddressClass="text-xs" />
180
+ </div>
181
+ </div>
182
+ </div>
183
+ {{/unless}}
184
+ </div>
185
+ </div>
123
186
  </div>
124
- {{/unless}}
125
- {{#if order.meta.tip}}
126
- <div class="flex items-center justify-between">
127
- <span class="dark:text-gray-50">{{t "storefront.component.widget.order.tip"}}</span>
128
- <span class="dark:text-gray-50">{{get-tip-amount order.meta.tip order.meta.subtotal order.meta.currency}}</span>
187
+ <div class="py-2 mt-2 space-y-2 border-t dark:border-gray-800">
188
+ <div class="flex items-center justify-between px-3 text-xs">
189
+ <span class="dark:text-gray-50">{{t "storefront.component.widget.orders.subtotal"}}</span>
190
+ <span class="dark:text-gray-50">{{format-currency order.meta.subtotal order.meta.currency}}</span>
191
+ </div>
192
+ {{#unless order.meta.is_pickup}}
193
+ <div class="flex items-center justify-between px-3 text-xs">
194
+ <span class="dark:text-gray-50">{{t "storefront.component.widget.orders.delivery-fee"}}</span>
195
+ <span class="dark:text-gray-50">{{format-currency order.meta.delivery_fee order.meta.currency}}</span>
196
+ </div>
197
+ {{/unless}}
198
+ {{#if order.meta.tip}}
199
+ <div class="flex items-center justify-between px-3 py-2 text-xs">
200
+ <span class="dark:text-gray-50">{{t "storefront.component.widget.orders.tip"}}</span>
201
+ <span class="dark:text-gray-50">{{get-tip-amount order.meta.tip order.meta.subtotal order.meta.currency}}</span>
202
+ </div>
203
+ {{/if}}
204
+ {{#if order.meta.delivery_tip}}
205
+ <div class="flex items-center justify-between px-3 text-xs">
206
+ <span class="dark:text-gray-50">{{t "storefront.component.widget.orders.delivery-tip"}}</span>
207
+ <span class="dark:text-gray-50">{{get-tip-amount order.meta.delivery_tip order.meta.subtotal order.meta.currency}}</span>
208
+ </div>
209
+ {{/if}}
210
+ <div class="flex items-center justify-between px-3 text-xs">
211
+ <span class="dark:text-gray-50 font-bold">{{t "storefront.component.widget.orders.total"}}</span>
212
+ <span class="dark:text-gray-50 font-bold">{{format-currency order.meta.total order.meta.currency}}</span>
213
+ </div>
129
214
  </div>
130
- {{/if}}
131
- {{#if order.meta.delivery_tip}}
132
- <div class="flex items-center justify-between">
133
- <span class="dark:text-gray-50">{{t "storefront.component.widget.order.delivery-tip"}}</span>
134
- <span class="dark:text-gray-50">{{get-tip-amount order.meta.delivery_tip order.meta.subtotal order.meta.currency}}</span>
135
- </div>
136
- {{/if}}
137
- <div class="flex items-center justify-between">
138
- <span class="dark:text-gray-50 font-bold">{{t "storefront.component.widget.order.tip"}}</span>
139
- <span class="dark:text-gray-50 font-bold">{{format-currency order.meta.total order.meta.currency}}</span>
140
- </div>
215
+ </ContentPanel>
141
216
  </div>
142
- </ContentPanel>
217
+ {{/each}}
143
218
  </div>
144
- {{/each}}
145
- </div>
146
- </ContentPanel>
219
+ {{else}}
220
+ <div class="table-wrapper table-fluid">
221
+ <table class="storefront-widget-table">
222
+ <thead>
223
+ <tr class="h-12 text-left py-1">
224
+ <th style={{"width: 15%"}}>{{t "storefront.component.widget.orders.id-column"}}</th>
225
+ <th style={{"width: 10%"}}>{{t "storefront.common.amount"}}</th>
226
+ <th style={{"width: 15%"}}>{{t "storefront.common.customer"}}</th>
227
+ <th style={{"width: 15%"}}>{{t "storefront.common.driver"}}</th>
228
+ <th style={{"width: 10%"}}>{{t "storefront.common.created"}}</th>
229
+ <th style={{"width: 15%"}}>{{t "storefront.common.status"}}</th>
230
+ <th style={{"width: 22%"}}></th>
231
+ </tr>
232
+ </thead>
233
+ <tbody>
234
+ {{#each this.orders as |order|}}
235
+ <tr class="h-12">
236
+ <td><a href="javascript:;" {{on "click" (fn this.viewOrder order)}}>{{order.public_id}}</a></td>
237
+ <td>{{format-currency order.meta.total order.meta.currency}}</td>
238
+ <td>{{n-a order.customer.name}}</td>
239
+ <td>
240
+ {{#if order.meta.is_pickup}}
241
+ <Badge @hideStatusDot={{true}} @status="info"><FaIcon @icon="hand-holding-dollar" class="mr-1" />{{t
242
+ "storefront.component.widget.orders.pickup-order"
243
+ }}</Badge>
244
+ {{else}}
245
+ {{n-a order.driver_assigned.name}}
246
+ {{/if}}
247
+ </td>
248
+ <td>{{order.createdAgo}}</td>
249
+ <td>
250
+ <Badge @status={{order.status}} />
251
+ </td>
252
+ <td>
253
+ <div class="flex flex-row justify-end space-x-2">
254
+ {{#if order.isFresh}}
255
+ <Button
256
+ @size="xs"
257
+ @type="success"
258
+ @iconPrefix="fas"
259
+ @icon="check"
260
+ @text={{t "storefront.component.widget.orders.accept-order"}}
261
+ @onClick={{fn this.acceptOrder order}}
262
+ />
263
+ {{/if}}
264
+ {{#if order.isPreparing}}
265
+ <Button
266
+ @size="xs"
267
+ @type="success"
268
+ @icon="bell-concierge"
269
+ @text={{t "storefront.component.widget.orders.mark-as-ready"}}
270
+ @onClick={{fn this.markAsReady order}}
271
+ />
272
+ {{/if}}
273
+ {{#if order.isPickupReady}}
274
+ <Button
275
+ @size="xs"
276
+ @type="success"
277
+ @icon="check"
278
+ @text={{t "storefront.component.widget.orders.mark-as-completed"}}
279
+ @onClick={{fn this.markAsCompleted order}}
280
+ />
281
+ {{/if}}
282
+ {{#unless order.isCanceled}}
283
+ <Button
284
+ @size="xs"
285
+ @type="danger"
286
+ @icon="ban"
287
+ @helpText={{t "storefront.component.widget.orders.cancel-order"}}
288
+ @onClick={{fn this.cancelOrder order}}
289
+ />
290
+ {{/unless}}
291
+ </div>
292
+ </td>
293
+ </tr>
294
+ {{/each}}
295
+
296
+ <tr class="h-12">
297
+ <td><div class="font-semibold text-green-500">TOTAL</div></td>
298
+ <td colspan="6"><div class="flex justify-start text-green-500 font-semibold">{{format-currency this.total this.currency}}</div></td>
299
+ </tr>
300
+ </tbody>
301
+ </table>
302
+ </div>
303
+ {{/if}}
304
+ </ContentPanel>
305
+ {{/if}}
@@ -1,9 +1,9 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
- import { inject as controller } from '@ember/controller';
5
- import { action, computed, get } from '@ember/object';
6
- import { later } from '@ember/runloop';
4
+ import { action, get } from '@ember/object';
5
+ import { debug } from '@ember/debug';
6
+ import { task } from 'ember-concurrency';
7
7
 
8
8
  export default class WidgetOrdersComponent extends Component {
9
9
  @service store;
@@ -13,35 +13,24 @@ export default class WidgetOrdersComponent extends Component {
13
13
  @service appCache;
14
14
  @service modalsManager;
15
15
  @service contextPanel;
16
- @tracked isLoading = true;
16
+ @service orderActions;
17
+ @service notifications;
17
18
  @tracked orders = [];
18
- @controller('orders.index.view') orderDetailsController;
19
+ @tracked title = this.intl.t('storefront.component.widget.orders.widget-title');
20
+ @tracked loaded = false;
21
+ @tracked total = 0;
19
22
 
20
- @computed('args.title') get title() {
21
- return this.args.title ?? this.intl.t('storefront.component.widget.orders.widget-title');
22
- }
23
-
24
- constructor() {
23
+ constructor(owner, { title }) {
25
24
  super(...arguments);
26
- }
27
-
28
- @action async setupWidget() {
29
- later(
30
- this,
31
- () => {
32
- this.reloadOrders();
33
- },
34
- 100
35
- );
36
-
37
- // reload orders when new order income
25
+ this.title = title ?? this.intl.t('storefront.component.widget.orders.widget-title');
26
+ this.currency = get(this.storefront, 'activeStore.currency');
27
+ // this.orders = this.getCachedOrders();
28
+ this.loadOrders.perform();
38
29
  this.storefront.on('order.broadcasted', () => {
39
- this.reloadOrders();
30
+ this.loadOrders.perform();
40
31
  });
41
-
42
- // reload orders when store changes
43
32
  this.storefront.on('storefront.changed', () => {
44
- this.reloadOrders();
33
+ this.loadOrders.perform();
45
34
  });
46
35
  }
47
36
 
@@ -49,61 +38,53 @@ export default class WidgetOrdersComponent extends Component {
49
38
  this.orders = await this.fetchOrders(params);
50
39
  }
51
40
 
52
- @action fetchOrders(params = {}) {
53
- let cachedOrders;
41
+ @task *loadOrders(params = {}) {
42
+ const storefront = get(this.storefront, 'activeStore.public_id');
43
+ const queryParams = {
44
+ storefront,
45
+ limit: 14,
46
+ sort: '-created_at',
47
+ ...params,
48
+ };
54
49
 
55
50
  try {
56
- if (this.appCache.has('storefront_recent_orders')) {
57
- cachedOrders = this.appCache.getEmberData('storefront_recent_orders', 'order');
58
- }
59
- } catch (exception) {
60
- // silent exception just load orders from
51
+ const orders = yield this.fetch.get('orders', queryParams, { namespace: 'storefront/int/v1', normalizeToEmberData: true });
52
+ this.loaded = true;
53
+ this.updateCachedOrders(orders);
54
+ this.total = this.calculateTotal(orders);
55
+ this.orders = orders;
56
+
57
+ return orders;
58
+ } catch (err) {
59
+ debug('Error loading orders for widget:', err);
61
60
  }
61
+ }
62
+
63
+ calculateTotal(orders = []) {
64
+ let total = 0;
65
+ orders.forEach((order) => {
66
+ total += get(order, 'meta.total');
67
+ });
62
68
 
63
- if (cachedOrders) {
64
- this.orders = cachedOrders;
69
+ return total;
70
+ }
71
+
72
+ getCachedOrders() {
73
+ let cachedOrders = [];
74
+ if (this.appCache.has('storefront_recent_orders')) {
75
+ cachedOrders = this.appCache.getEmberData('storefront_recent_orders', 'order');
65
76
  }
66
77
 
67
- this.isLoading = true;
68
-
69
- return new Promise((resolve) => {
70
- const storefront = get(this.storefront, 'activeStore.public_id');
71
-
72
- if (!storefront) {
73
- this.isLoading = false;
74
- return resolve([]);
75
- }
76
-
77
- const queryParams = {
78
- storefront,
79
- limit: 14,
80
- sort: '-created_at',
81
- ...params,
82
- };
83
-
84
- this.fetch
85
- .get('orders', queryParams, {
86
- namespace: 'storefront/int/v1',
87
- normalizeToEmberData: true,
88
- })
89
- .then((orders) => {
90
- this.isLoading = false;
91
-
92
- try {
93
- this.appCache.setEmberData('storefront_recent_orders', orders, ['tracking_statuses', 'tracking_number']);
94
- } catch (exception) {
95
- // silent exception just clear from cache if not able to set
96
- this.appCache.set('storefront_recent_orders', undefined);
97
- }
98
-
99
- resolve(orders);
100
- })
101
- .catch(() => {
102
- this.isLoading = false;
103
-
104
- resolve(this.orders);
105
- });
106
- });
78
+ return cachedOrders;
79
+ }
80
+
81
+ updateCachedOrders(orders = []) {
82
+ try {
83
+ this.appCache.setEmberData('storefront_recent_orders', orders, ['tracking_statuses', 'tracking_number']);
84
+ } catch (err) {
85
+ this.appCache.set('storefront_recent_orders', undefined);
86
+ debug('Error updating orders widget cache:', err);
87
+ }
107
88
  }
108
89
 
109
90
  @action async viewOrder(order) {
@@ -111,18 +92,32 @@ export default class WidgetOrdersComponent extends Component {
111
92
  }
112
93
 
113
94
  @action async acceptOrder(order) {
114
- await this.orderDetailsController.acceptOrder(order);
95
+ await this.orderActions.acceptOrder(order, () => {
96
+ this.loadOrders.perform();
97
+ });
115
98
  }
116
99
 
117
100
  @action markAsReady(order) {
118
- this.orderDetailsController.markAsReady(order);
101
+ this.orderActions.markAsReady(order, () => {
102
+ this.loadOrders.perform();
103
+ });
119
104
  }
120
105
 
121
106
  @action markAsCompleted(order) {
122
- this.orderDetailsController.markAsCompleted(order);
107
+ this.orderActions.markAsCompleted(order, () => {
108
+ this.loadOrders.perform();
109
+ });
123
110
  }
124
111
 
125
- @action async assignDriver(order) {
126
- await this.orderDetailsController.assignDriver(order);
112
+ @action assignDriver(order) {
113
+ this.orderActions.assignDriver(order, () => {
114
+ this.loadOrders.perform();
115
+ });
116
+ }
117
+
118
+ @action cancelOrder(order) {
119
+ this.orderActions.cancelOrder(order, () => {
120
+ this.loadOrders.perform();
121
+ });
127
122
  }
128
123
  }