@fleetbase/storefront-engine 0.3.12 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/add-product-as-entity-button.js +3 -3
- package/addon/components/modals/create-first-store.hbs +3 -1
- package/addon/components/widget/storefront-key-metrics.hbs +1 -1
- package/addon/controllers/application.js +1 -1
- package/addon/controllers/products/index/category/edit.js +1 -0
- package/addon/controllers/products/index/category/new.js +6 -2
- package/addon/routes/application.js +14 -3
- package/addon/routes/customers/index/edit.js +14 -1
- package/addon/routes/customers/index/view.js +11 -0
- package/addon/routes/customers/index.js +11 -0
- package/addon/routes/networks/index/network.js +11 -0
- package/addon/routes/networks/index.js +11 -0
- package/addon/routes/orders/index/edit.js +14 -1
- package/addon/routes/orders/index/new.js +14 -1
- package/addon/routes/orders/index/view.js +10 -0
- package/addon/routes/orders/index.js +11 -0
- package/addon/routes/products/index/category/edit.js +11 -0
- package/addon/routes/products/index/category/new.js +15 -1
- package/addon/routes/products/index/category.js +12 -1
- package/addon/routes/products/index/index.js +11 -0
- package/addon/routes/products/index.js +11 -0
- package/addon/routes/settings/index.js +11 -0
- package/addon/services/storefront.js +35 -74
- package/addon/templates/application.hbs +31 -5
- package/addon/templates/customers/index.hbs +1 -1
- package/addon/templates/networks/index.hbs +4 -3
- package/addon/templates/orders/index.hbs +2 -2
- package/addon/templates/products/index/category/new.hbs +326 -245
- package/addon/templates/products/index/category.hbs +6 -9
- package/addon/templates/products/index/index.hbs +2 -2
- package/addon/templates/products/index.hbs +11 -5
- package/addon/templates/settings/index.hbs +1 -0
- package/composer.json +3 -3
- package/extension.json +1 -1
- package/package.json +8 -7
- package/server/src/Auth/Schemas/Storefront.php +97 -3
- package/server/src/Http/Controllers/v1/CartController.php +1 -1
- package/server/src/Http/Controllers/v1/CustomerController.php +1 -1
- package/server/src/Http/Resources/Product.php +5 -5
- package/server/src/Models/Cart.php +1 -1
- package/server/src/Models/Network.php +3 -3
- package/server/src/Models/Store.php +4 -4
- package/server/src/Support/Metrics.php +8 -8
- package/server/src/Support/QPay.php +4 -4
|
@@ -13,293 +13,374 @@
|
|
|
13
13
|
@text={{this.overlayActionButtonTitle}}
|
|
14
14
|
@onClick={{perform this.saveProduct}}
|
|
15
15
|
@isLoading={{not this.saveProduct.isIdle}}
|
|
16
|
+
@permission={{this.abilityPermission}}
|
|
16
17
|
/>
|
|
17
18
|
</Overlay::Header>
|
|
18
19
|
|
|
19
20
|
<Overlay::Body @increaseInnerBodyHeightBy="0" @wrapperClass="new-order-overlay-body px-4 space-y-4 pt-4">
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
<InputGroup @name="Product Tags">
|
|
25
|
-
<TagInput
|
|
26
|
-
class="form-input"
|
|
27
|
-
@placeholder="Add tags"
|
|
28
|
-
@allowSpacesInTags={{true}}
|
|
29
|
-
@tags={{this.product.tags}}
|
|
30
|
-
@addTag={{this.addTag}}
|
|
31
|
-
@removeTagAtIndex={{this.removeTag}}
|
|
32
|
-
as |tag|
|
|
33
|
-
>
|
|
34
|
-
{{tag}}
|
|
35
|
-
</TagInput>
|
|
36
|
-
</InputGroup>
|
|
37
|
-
<InputGroup @name="Product SKU" @value={{this.product.sku}} @helpText="Enter product SKU if applicable" />
|
|
38
|
-
<div class="grid grid-cols-2 gap-2">
|
|
39
|
-
<InputGroup @name="Price" @helpText="Enter a price users will pay to purchase this product">
|
|
40
|
-
<MoneyInput
|
|
41
|
-
class="w-full"
|
|
42
|
-
@currency={{if this.product.currency this.product.currency this.activeStore.currency}}
|
|
43
|
-
@value={{this.product.price}}
|
|
44
|
-
@canSelectCurrency={{false}}
|
|
45
|
-
@onCurrencyChange={{fn (mut this.product.currency)}}
|
|
46
|
-
/>
|
|
21
|
+
{{#let (cannot this.abilityPermission) as |unauthorized|}}
|
|
22
|
+
<InputGroup @name="Product Name" @value={{this.product.name}} @helpText="Enter your product's name" @disabled={{unauthorized}} />
|
|
23
|
+
<InputGroup @name="Product Description" @helpText="Enter a description of your product">
|
|
24
|
+
<Textarea @value={{this.product.description}} class="form-input w-full" placeholder="Enter a description of your product...." disabled={{unauthorized}} rows={{4}} />
|
|
47
25
|
</InputGroup>
|
|
48
|
-
<InputGroup @name="
|
|
49
|
-
<
|
|
50
|
-
class="
|
|
51
|
-
@
|
|
52
|
-
@
|
|
53
|
-
@
|
|
54
|
-
@
|
|
55
|
-
|
|
26
|
+
<InputGroup @name="Product Tags">
|
|
27
|
+
<TagInput
|
|
28
|
+
class="form-input"
|
|
29
|
+
@placeholder="Add tags"
|
|
30
|
+
@allowSpacesInTags={{true}}
|
|
31
|
+
@tags={{this.product.tags}}
|
|
32
|
+
@addTag={{this.addTag}}
|
|
33
|
+
@removeTagAtIndex={{this.removeTag}}
|
|
34
|
+
@disabled={{unauthorized}}
|
|
35
|
+
as |tag|
|
|
36
|
+
>
|
|
37
|
+
{{tag}}
|
|
38
|
+
</TagInput>
|
|
56
39
|
</InputGroup>
|
|
57
|
-
|
|
40
|
+
<InputGroup @name="Product SKU" @value={{this.product.sku}} @helpText="Enter product SKU if applicable" @disabled={{unauthorized}} />
|
|
41
|
+
<div class="grid grid-cols-2 gap-2">
|
|
42
|
+
<InputGroup @name="Price" @helpText="Enter a price users will pay to purchase this product">
|
|
43
|
+
<MoneyInput
|
|
44
|
+
class="w-full"
|
|
45
|
+
@currency={{if this.product.currency this.product.currency this.activeStore.currency}}
|
|
46
|
+
@value={{this.product.price}}
|
|
47
|
+
@canSelectCurrency={{false}}
|
|
48
|
+
@onCurrencyChange={{fn (mut this.product.currency)}}
|
|
49
|
+
@disabled={{unauthorized}}
|
|
50
|
+
/>
|
|
51
|
+
</InputGroup>
|
|
52
|
+
<InputGroup @name="Sale Price" @helpText="Optionally add a sale price for the product if the product is put on sale">
|
|
53
|
+
<MoneyInput
|
|
54
|
+
class="w-full"
|
|
55
|
+
@currency={{if this.product.currency this.product.currency this.activeStore.currency}}
|
|
56
|
+
@value={{this.product.sale_price}}
|
|
57
|
+
@canSelectCurrency={{false}}
|
|
58
|
+
@onCurrencyChange={{fn (mut this.product.currency)}}
|
|
59
|
+
@disabled={{unauthorized}}
|
|
60
|
+
/>
|
|
61
|
+
</InputGroup>
|
|
62
|
+
</div>
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
<ContentPanel
|
|
65
|
+
@title="Metadata"
|
|
66
|
+
@open={{this.product.meta_array.length}}
|
|
67
|
+
@actionButtons={{this.metadataButtons}}
|
|
68
|
+
@pad={{true}}
|
|
69
|
+
@panelBodyWrapperClass="px-0 py-4"
|
|
70
|
+
@panelBodyClass="bg-white dark:bg-gray-800"
|
|
71
|
+
>
|
|
72
|
+
{{#each this.product.meta_array as |metaField index|}}
|
|
73
|
+
<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700">
|
|
74
|
+
<div class="input-group">
|
|
75
|
+
<div class="flex justify-between items-center mb-1">
|
|
76
|
+
<Input
|
|
77
|
+
class="form-input border-0 px-2 py-1 m-0 bg-white dark:bg-gray-900 shadow-none"
|
|
78
|
+
@value={{metaField.label}}
|
|
79
|
+
disabled={{unauthorized}}
|
|
80
|
+
placeholder={{metaField.label}}
|
|
81
|
+
/>
|
|
82
|
+
<a href="javascript:;" class="text-xs" tabindex="-1" disabled={{unauthorized}} {{on "click" (fn this.removeMetaField index)}}>
|
|
83
|
+
<FaIcon @icon="trash" @size="sm" class="mr-1" />
|
|
84
|
+
Remove
|
|
85
|
+
</a>
|
|
86
|
+
</div>
|
|
87
|
+
<Input class="w-full form-input" @value={{metaField.value}} placeholder={{metaField.label}} disabled={{unauthorized}} />
|
|
76
88
|
</div>
|
|
77
|
-
<Input class="w-full form-input" @value={{metaField.value}} placeholder={{metaField.label}} />
|
|
78
89
|
</div>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
</ContentPanel>
|
|
82
|
-
|
|
83
|
-
<InputGroup>
|
|
84
|
-
<TranslationsEditor @value={{this.product.translations}} @defaultKeys={{array "name" "description"}} @onChange={{fn (mut this.product.translations)}} />
|
|
85
|
-
</InputGroup>
|
|
90
|
+
{{/each}}
|
|
91
|
+
</ContentPanel>
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
<InputGroup>
|
|
94
|
+
<TranslationsEditor
|
|
95
|
+
@value={{this.product.translations}}
|
|
96
|
+
@defaultKeys={{array "name" "description"}}
|
|
97
|
+
@onChange={{fn (mut this.product.translations)}}
|
|
98
|
+
@disabled={{unauthorized}}
|
|
99
|
+
/>
|
|
100
|
+
</InputGroup>
|
|
93
101
|
|
|
94
|
-
|
|
102
|
+
<div class="store-boolean-settings">
|
|
95
103
|
<div class="input-group mb-0">
|
|
96
|
-
<Checkbox @value={{this.product.
|
|
97
|
-
<FaIcon @icon="
|
|
104
|
+
<Checkbox @value={{this.product.is_service}} @onToggle={{fn (mut this.product.is_service)}} @disabled={{unauthorized}}>
|
|
105
|
+
<FaIcon @icon="house-leave" class="text-indigo-400 mx-2" /><span class="dark:text-gray-100 text-sm">This is a service</span>
|
|
98
106
|
</Checkbox>
|
|
99
107
|
</div>
|
|
100
|
-
{{/if}}
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
{{#if this.product.is_service}}
|
|
110
|
+
<div class="input-group mb-0">
|
|
111
|
+
<Checkbox @value={{this.product.is_bookable}} @onToggle={{fn (mut this.product.is_bookable)}} @disabled={{unauthorized}}>
|
|
112
|
+
<FaIcon @icon="calendar-star" class="text-yellow-400 mx-2" /><span class="dark:text-gray-100 text-sm">This service is bookable</span>
|
|
113
|
+
</Checkbox>
|
|
114
|
+
</div>
|
|
115
|
+
{{/if}}
|
|
107
116
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
<div class="input-group mb-0">
|
|
118
|
+
<Checkbox @value={{this.product.is_on_sale}} @onToggle={{fn (mut this.product.is_on_sale)}} @disabled={{unauthorized}}>
|
|
119
|
+
<FaIcon @icon="badge-percent" class="text-blue-400 mx-2" /><span class="dark:text-gray-100 text-sm">This product is on sale</span>
|
|
120
|
+
</Checkbox>
|
|
121
|
+
</div>
|
|
113
122
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
</div>
|
|
123
|
+
<div class="input-group">
|
|
124
|
+
<Checkbox @value={{this.product.is_recommended}} @onToggle={{fn (mut this.product.is_recommended)}} @disabled={{unauthorized}}>
|
|
125
|
+
<FaIcon @icon="check" class="text-green-400 mx-2" /><span class="dark:text-gray-100 text-sm">This product is recommended</span>
|
|
126
|
+
</Checkbox>
|
|
127
|
+
</div>
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
<div class="input-group">
|
|
130
|
+
<Checkbox @value={{this.product.is_available}} @onToggle={{fn (mut this.product.is_available)}} @disabled={{unauthorized}}>
|
|
131
|
+
<FaIcon @icon="eye" class="text-blue-400 mx-2" /><span class="dark:text-gray-100 text-sm">This product is available</span>
|
|
132
|
+
</Checkbox>
|
|
125
133
|
</div>
|
|
126
134
|
</div>
|
|
127
|
-
<Tabs as |Tab|>
|
|
128
|
-
{{#each this.product.variants as |variant|}}
|
|
129
|
-
<Tab @title={{variant.name}}>
|
|
130
|
-
<div class="px-4 py-3">
|
|
131
|
-
<div class="flex items-center justify-end mb-3">
|
|
132
|
-
<Button class="mr-2" @type="danger" @text="Remove Variant" @icon="trash" @onClick={{fn this.removeProductVariant variant}} />
|
|
133
|
-
<Button class="mr-2" @type="warning" @text="Edit Variant" @icon="pencil" @onClick={{fn this.editProductVariant variant}} />
|
|
134
|
-
<Button @text={{concat "New " (lowercase variant.name) " option"}} @icon="plus" @iconPrefix="fas" @onClick={{fn this.addVariantOption variant}} />
|
|
135
|
-
</div>
|
|
136
|
-
<div class="space-y-2">
|
|
137
|
-
{{#each variant.options as |variantOption index|}}
|
|
138
|
-
<div class="grid grid-cols-9 gap-1 px-3 py-2 rounded-md dark:bg-gray-900 bg-gray-50">
|
|
139
|
-
<div class="col-span-2">
|
|
140
|
-
<Input @type="text" @value={{variantOption.name}} class="form-input w-full" placeholder="Option Name" />
|
|
141
|
-
</div>
|
|
142
|
-
<div class="col-span-4">
|
|
143
|
-
<Input @type="text" @value={{variantOption.description}} class="form-input w-full" placeholder="Option Description" />
|
|
144
|
-
</div>
|
|
145
|
-
<div class="col-span-2">
|
|
146
|
-
<MoneyInput
|
|
147
|
-
class="w-full"
|
|
148
|
-
@currency={{if this.product.currency this.product.currency this.activeStore.currency}}
|
|
149
|
-
@canSelectCurrency={{false}}
|
|
150
|
-
@value={{variantOption.additional_cost}}
|
|
151
|
-
/>
|
|
152
|
-
</div>
|
|
153
|
-
<div class="flex items-center justify-center text-center text-sm">
|
|
154
|
-
<a href="javascript:;" {{on "click" (fn this.removeVariantOption variant index)}}>Remove</a>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
{{/each}}
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
</Tab>
|
|
161
|
-
{{/each}}
|
|
162
|
-
</Tabs>
|
|
163
|
-
</ContentPanel>
|
|
164
135
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
136
|
+
<ContentPanel @title="Variants" @open={{true}} @pad={{false}} @panelBodyWrapperClass="px-0 py-4" @panelBodyClass="bg-white dark:bg-gray-800">
|
|
137
|
+
<div class="content-panel-body p-4">
|
|
138
|
+
<div class="flex items-center justify-end">
|
|
139
|
+
<Button @text="New Variant" @icon="plus" @iconPrefix="fas" @onClick={{this.createProductVariant}} @permission="storefront create product-variant" />
|
|
140
|
+
</div>
|
|
169
141
|
</div>
|
|
170
|
-
<
|
|
171
|
-
{{#each this.product.
|
|
172
|
-
<
|
|
173
|
-
<div class="
|
|
174
|
-
<div class="flex-
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
142
|
+
<Tabs as |Tab|>
|
|
143
|
+
{{#each this.product.variants as |variant|}}
|
|
144
|
+
<Tab @title={{variant.name}}>
|
|
145
|
+
<div class="px-4 py-3">
|
|
146
|
+
<div class="flex items-center justify-end mb-3">
|
|
147
|
+
<Button
|
|
148
|
+
class="mr-2"
|
|
149
|
+
@type="danger"
|
|
150
|
+
@text="Remove Variant"
|
|
151
|
+
@icon="trash"
|
|
152
|
+
@onClick={{fn this.removeProductVariant variant}}
|
|
153
|
+
@permission="storefront delete product-variant"
|
|
154
|
+
/>
|
|
155
|
+
<Button
|
|
156
|
+
class="mr-2"
|
|
157
|
+
@type="warning"
|
|
158
|
+
@text="Edit Variant"
|
|
159
|
+
@icon="pencil"
|
|
160
|
+
@onClick={{fn this.editProductVariant variant}}
|
|
161
|
+
@permission="storefront update product-variant"
|
|
162
|
+
/>
|
|
163
|
+
<Button
|
|
164
|
+
@text={{concat "New " (lowercase variant.name) " option"}}
|
|
165
|
+
@icon="plus"
|
|
166
|
+
@iconPrefix="fas"
|
|
167
|
+
@onClick={{fn this.addVariantOption variant}}
|
|
168
|
+
@permission="storefront create product-variant-option"
|
|
169
|
+
/>
|
|
179
170
|
</div>
|
|
180
|
-
<div class="
|
|
181
|
-
|
|
171
|
+
<div class="space-y-2">
|
|
172
|
+
{{#each variant.options as |variantOption index|}}
|
|
173
|
+
<div class="grid grid-cols-9 gap-1 px-3 py-2 rounded-md dark:bg-gray-900 bg-gray-50">
|
|
174
|
+
<div class="col-span-2">
|
|
175
|
+
<Input
|
|
176
|
+
@type="text"
|
|
177
|
+
@value={{variantOption.name}}
|
|
178
|
+
class="form-input w-full"
|
|
179
|
+
placeholder="Option Name"
|
|
180
|
+
disabled={{cannot "storefront update product-variant-option"}}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
<div class="col-span-4">
|
|
184
|
+
<Input
|
|
185
|
+
@type="text"
|
|
186
|
+
@value={{variantOption.description}}
|
|
187
|
+
class="form-input w-full"
|
|
188
|
+
placeholder="Option Description"
|
|
189
|
+
disabled={{cannot "storefront update product-variant-option"}}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="col-span-2">
|
|
193
|
+
<MoneyInput
|
|
194
|
+
class="w-full"
|
|
195
|
+
@currency={{if this.product.currency this.product.currency this.activeStore.currency}}
|
|
196
|
+
@canSelectCurrency={{false}}
|
|
197
|
+
@value={{variantOption.additional_cost}}
|
|
198
|
+
@disabled={{cannot "storefront update product-variant-option"}}
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="flex items-center justify-center text-center text-sm">
|
|
202
|
+
<a
|
|
203
|
+
href="javascript:;"
|
|
204
|
+
{{on "click" (fn this.removeVariantOption variant index)}}
|
|
205
|
+
disabled={{cannot "storefront delete product-variant-option"}}
|
|
206
|
+
>Remove</a>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
{{/each}}
|
|
182
210
|
</div>
|
|
183
211
|
</div>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
</Tab>
|
|
213
|
+
{{/each}}
|
|
214
|
+
</Tabs>
|
|
215
|
+
</ContentPanel>
|
|
216
|
+
|
|
217
|
+
<ContentPanel @title="Add-Ons" @open={{true}} @pad={{false}} @panelBodyWrapperClass="px-0 py-4" @panelBodyClass="bg-white dark:bg-gray-800">
|
|
218
|
+
<div class="px-4">
|
|
219
|
+
<div class="flex items-center justify-end mb-3">
|
|
220
|
+
<Button
|
|
221
|
+
@text="Select Addon Categories"
|
|
222
|
+
@icon="plus"
|
|
223
|
+
@iconPrefix="fas"
|
|
224
|
+
@onClick={{perform this.promptSelectAddonCategories}}
|
|
225
|
+
@permission="storefront list product-addon"
|
|
226
|
+
/>
|
|
227
|
+
</div>
|
|
228
|
+
<div class="space-y-2">
|
|
229
|
+
{{#each this.product.addon_categories as |addonCategory index|}}
|
|
230
|
+
<div>
|
|
231
|
+
<div class="flex items-center rounded-md shadow-sm px-3 py-2 font-semibold bg-gray-200 dark:bg-gray-900 dark:text-gray-100 mb-2">
|
|
232
|
+
<div class="flex-1 flex items-center">
|
|
233
|
+
<FaIcon @icon="pencil" class="mr-1 dark:text-gray-100" />
|
|
234
|
+
<div class="w-full px-2 m-0 border-none bg-transparent dark:text-gray-100">
|
|
235
|
+
{{addonCategory.name}}
|
|
198
236
|
</div>
|
|
199
237
|
</div>
|
|
200
|
-
|
|
238
|
+
<div class="flex items-center">
|
|
239
|
+
<a
|
|
240
|
+
href="javascript:;"
|
|
241
|
+
class="destroy-action opacity-50 hover:opacity-100 text-sm"
|
|
242
|
+
{{on "click" (fn this.removeAddonCategory index)}}
|
|
243
|
+
disabled={{cannot "storefront delete product-addon"}}
|
|
244
|
+
>Remove</a>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
<div class="space-y-2 mb-2">
|
|
248
|
+
{{#each addonCategory.category.addons as |addon|}}
|
|
249
|
+
<div class="grid grid-cols-10 gap-1 px-3 py-2 rounded-md border border-gray-200 dark:border-gray-900 dark:text-gray-100 dark:bg-gray-800 bg-gray-200">
|
|
250
|
+
<div class="flex items-center">
|
|
251
|
+
<Checkbox
|
|
252
|
+
@value={{not (in-array addon.id addonCategory.excluded_addons)}}
|
|
253
|
+
@onToggle={{fn this.excludeAddon index addon}}
|
|
254
|
+
disabled={{cannot "storefront update product-addon"}}
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
257
|
+
<div class="col-span-3 flex items-center">
|
|
258
|
+
{{addon.name}}
|
|
259
|
+
</div>
|
|
260
|
+
<div class="col-span-4">
|
|
261
|
+
{{addon.description}}
|
|
262
|
+
</div>
|
|
263
|
+
<div class="col-span-2">
|
|
264
|
+
{{format-currency addon.price}}
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
{{/each}}
|
|
268
|
+
</div>
|
|
201
269
|
</div>
|
|
202
|
-
|
|
203
|
-
|
|
270
|
+
{{/each}}
|
|
271
|
+
</div>
|
|
204
272
|
</div>
|
|
205
|
-
</
|
|
206
|
-
</ContentPanel>
|
|
273
|
+
</ContentPanel>
|
|
207
274
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
275
|
+
<ContentPanel @title="Availability" @open={{this.product.hours.length}} @pad={{false}} @panelBodyWrapperClass="px-0 py-4" @panelBodyClass="bg-white dark:bg-gray-800">
|
|
276
|
+
<div class="px-4 py-3">
|
|
277
|
+
<ScheduleManager
|
|
278
|
+
@subject={{this.product}}
|
|
279
|
+
@subjectKey="product_uuid"
|
|
280
|
+
@hourModelType="product-hour"
|
|
281
|
+
@disabled={{unauthorized}}
|
|
282
|
+
class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-2"
|
|
283
|
+
/>
|
|
284
|
+
</div>
|
|
285
|
+
</ContentPanel>
|
|
213
286
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
287
|
+
<ContentPanel @title="Images & Videos" @open={{this.product.files.length}} @pad={{false}} @panelBodyWrapperClass="px-0 py-4" @panelBodyClass="bg-white dark:bg-gray-800">
|
|
288
|
+
<div class="px-6 space-y-4">
|
|
289
|
+
{{#if this.isUploading}}
|
|
290
|
+
<div
|
|
291
|
+
class="min-h-56 dropzone w-full rounded-lg px-4 py-8 min-h bg-gray-50 dark:bg-gray-900 bg-opacity-25 text-gray-900 dark:text-white text-center flex flex-col items-center justify-center border-2 border-dashed border-gray-200 dark:border-indigo-500"
|
|
292
|
+
>
|
|
293
|
+
<div class="flex items-center justify-center py-5">
|
|
294
|
+
<Spinner class="text-sm dar:text-gray-100" @loadingMessage={{t "component.dropzone.uploading"}} />
|
|
295
|
+
</div>
|
|
222
296
|
</div>
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
{{
|
|
297
|
+
{{else}}
|
|
298
|
+
{{#let (file-queue name="files" onFileAdded=this.queueFile accept=(join "," this.acceptedFileTypes)) as |queue|}}
|
|
299
|
+
<FileDropzone @queue={{queue}} @disabled={{unauthorized}} class="dropzone file-dropzone" as |dropzone|>
|
|
300
|
+
{{#if dropzone.active}}
|
|
301
|
+
{{#if dropzone.valid}}
|
|
302
|
+
{{t "component.dropzone.drop-to-upload"}}
|
|
303
|
+
{{else}}
|
|
304
|
+
{{t "component.dropzone.invalid"}}
|
|
305
|
+
{{/if}}
|
|
306
|
+
{{else if queue.files.length}}
|
|
307
|
+
<div class="my-2">
|
|
308
|
+
<FaIcon @icon="photo-video" class="text-indigo-500 mr-2" />
|
|
309
|
+
{{t "component.dropzone.files-ready-for-upload" numOfFiles=(pluralize queue.files.length (t "component.dropzone.file"))}}
|
|
310
|
+
</div>
|
|
311
|
+
<div class="my-2">({{queue.progress}}%)</div>
|
|
230
312
|
{{else}}
|
|
231
|
-
|
|
313
|
+
<h4 class="font-semibold mb-8">
|
|
314
|
+
<FaIcon @icon="photo-video" @size="2x" class="text-indigo-500 mr-2" />
|
|
315
|
+
{{t "component.dropzone.upload-images-videos"}}
|
|
316
|
+
</h4>
|
|
317
|
+
<div>
|
|
318
|
+
{{#if dropzone.supported}}
|
|
319
|
+
<p class="text-base font-semibold my-5">{{t "component.dropzone.dropzone-supported-images-videos"}}</p>
|
|
320
|
+
{{/if}}
|
|
321
|
+
<FileUpload
|
|
322
|
+
@name="files"
|
|
323
|
+
@for="files"
|
|
324
|
+
@accept={{join "," this.acceptedFileTypes}}
|
|
325
|
+
@multiple={{true}}
|
|
326
|
+
@onFileAdded={{this.queueFile}}
|
|
327
|
+
@disabled={{unauthorized}}
|
|
328
|
+
>
|
|
329
|
+
<a tabindex={{0}} class="btn btn-magic cursor-pointer ml-1">{{t "component.dropzone.or-select-button-text"}}</a>
|
|
330
|
+
</FileUpload>
|
|
331
|
+
</div>
|
|
232
332
|
{{/if}}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
333
|
+
</FileDropzone>
|
|
334
|
+
{{/let}}
|
|
335
|
+
{{#if this.uploadQueue}}
|
|
336
|
+
<div class="mx-4">
|
|
337
|
+
<div class="flex items-center justify-between mb-4">
|
|
338
|
+
<span class="leading-6 dark:text-gray-100">{{t "component.dropzone.upload-queue"}}</span>
|
|
237
339
|
</div>
|
|
238
|
-
<div class="
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
{{/
|
|
248
|
-
<FileUpload @name="files" @for="files" @accept={{join "," this.acceptedFileTypes}} @multiple={{true}} @onFileAdded={{this.queueFile}}>
|
|
249
|
-
<a tabindex={{0}} class="btn btn-magic cursor-pointer ml-1">{{t "component.dropzone.or-select-button-text"}}</a>
|
|
250
|
-
</FileUpload>
|
|
340
|
+
<div class="space-y-2 mb-4">
|
|
341
|
+
{{#each this.uploadQueue as |file|}}
|
|
342
|
+
<div class="flex items-center justify-between bg-blue-100 border border-blue-800 dark:border-blue-800 py-1.5 shadow-sm rounded-lg px-4">
|
|
343
|
+
<div class="text-xs text-blue-900 truncate">{{truncate-filename file.name 50}}</div>
|
|
344
|
+
<div class="flex items-center text-sm">
|
|
345
|
+
<Spinner class="text-blue-900 mr-2" />
|
|
346
|
+
<span class="font-bold text-blue-900">{{round file.progress}}%</span>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
{{/each}}
|
|
251
350
|
</div>
|
|
252
|
-
{{/if}}
|
|
253
|
-
</FileDropzone>
|
|
254
|
-
{{/let}}
|
|
255
|
-
{{#if this.uploadQueue}}
|
|
256
|
-
<div class="mx-4">
|
|
257
|
-
<div class="flex items-center justify-between mb-4">
|
|
258
|
-
<span class="leading-6 dark:text-gray-100">{{t "component.dropzone.upload-queue"}}</span>
|
|
259
351
|
</div>
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
<
|
|
352
|
+
{{/if}}
|
|
353
|
+
<div>
|
|
354
|
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-2 md:gap-4">
|
|
355
|
+
{{#each this.product.files as |file|}}
|
|
356
|
+
<FileRecord @file={{file}} @fileIconClass={{if (eq this.product.primary_image_uuid file.id) "border-blue-400"}} @onDelete={{this.removeFile}}>
|
|
357
|
+
<div class="flex items-center justify-evenly">
|
|
358
|
+
<Button
|
|
359
|
+
@icon="magic"
|
|
360
|
+
@text="Make Primary"
|
|
361
|
+
@size="xs"
|
|
362
|
+
@textClass="text-xs truncate"
|
|
363
|
+
@onClick={{fn this.makePrimaryFile file}}
|
|
364
|
+
@disabled={{or unauthorized (eq this.product.primary_image_uuid file.id)}}
|
|
365
|
+
/>
|
|
267
366
|
</div>
|
|
268
|
-
</
|
|
367
|
+
</FileRecord>
|
|
269
368
|
{{/each}}
|
|
270
369
|
</div>
|
|
271
370
|
</div>
|
|
272
371
|
{{/if}}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
{{#each this.product.files as |file|}}
|
|
276
|
-
<FileRecord @file={{file}} @fileIconClass={{if (eq this.product.primary_image_uuid file.id) "border-blue-400"}} @onDelete={{this.removeFile}}>
|
|
277
|
-
<div class="flex items-center justify-evenly">
|
|
278
|
-
<Button
|
|
279
|
-
@icon="magic"
|
|
280
|
-
@text="Make Primary"
|
|
281
|
-
@size="xs"
|
|
282
|
-
@textClass="text-xs truncate"
|
|
283
|
-
@onClick={{fn this.makePrimaryFile file}}
|
|
284
|
-
@disabled={{eq this.product.primary_image_uuid file.id}}
|
|
285
|
-
/>
|
|
286
|
-
</div>
|
|
287
|
-
</FileRecord>
|
|
288
|
-
{{/each}}
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
291
|
-
{{/if}}
|
|
292
|
-
</div>
|
|
293
|
-
</ContentPanel>
|
|
372
|
+
</div>
|
|
373
|
+
</ContentPanel>
|
|
294
374
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
375
|
+
<ContentPanel @title="Youtube Videos" @open={{this.product.youtube_urls.length}} @wrapperClass="mb-12" @panelBodyWrapperClass="p-4" @panelBodyClass="bg-white dark:bg-gray-800">
|
|
376
|
+
<p class="text-sm">Add YouTube video urls to assosciate with this product or service.</p>
|
|
377
|
+
<div class="input-group">
|
|
378
|
+
<ArrayInput @data={{this.product.youtube_urls}} @placeholder="Enter youtube video url" @onDataChanged={{fn (mut this.product.youtube_urls)}} @disabled={{unauthorized}}>
|
|
379
|
+
<InputLabel @labelText="Youtube Video URLs" @helpText="Input product youtube video urls if applicable." />
|
|
380
|
+
</ArrayInput>
|
|
381
|
+
</div>
|
|
382
|
+
</ContentPanel>
|
|
383
|
+
<Spacer @height="300px" />
|
|
384
|
+
{{/let}}
|
|
304
385
|
</Overlay::Body>
|
|
305
386
|
</Overlay>
|