cloudcommerce 2.4.2 → 2.5.0

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 (77) hide show
  1. package/.github/workflows/test-apps.yml +2 -2
  2. package/CHANGELOG.md +28 -0
  3. package/action.yml +3 -3
  4. package/ecomplus-stores/barradoce/functions/many/package.json +3 -3
  5. package/ecomplus-stores/barradoce/functions/ssr/package.json +10 -8
  6. package/ecomplus-stores/barradoce/functions/ssr/src/components/Pagination.vue +4 -7
  7. package/ecomplus-stores/barradoce/functions/ssr/src/components/ProductDetails.vue +1 -1
  8. package/ecomplus-stores/barradoce/functions/ssr/src/layouts/PageHeader.astro +5 -4
  9. package/ecomplus-stores/barradoce/functions/ssr/src/main/content/Hero.astro +1 -1
  10. package/ecomplus-stores/barradoce/functions/ssr/src/main/content/Sections.astro +10 -7
  11. package/ecomplus-stores/barradoce/functions/ssr/src/pages/admin/index.astro +25 -0
  12. package/ecomplus-stores/barradoce/functions/with-apps/package.json +3 -3
  13. package/ecomplus-stores/barradoce/package.json +2 -2
  14. package/package.json +10 -10
  15. package/packages/api/package.json +1 -1
  16. package/packages/apps/affiliate-program/package.json +1 -1
  17. package/packages/apps/correios/package.json +2 -2
  18. package/packages/apps/custom-payment/package.json +1 -1
  19. package/packages/apps/custom-shipping/package.json +1 -1
  20. package/packages/apps/datafrete/package.json +1 -1
  21. package/packages/apps/discounts/package.json +1 -1
  22. package/packages/apps/emails/package.json +1 -1
  23. package/packages/apps/fb-conversions/package.json +1 -1
  24. package/packages/apps/flash-courier/package.json +1 -1
  25. package/packages/apps/frenet/package.json +1 -1
  26. package/packages/apps/galaxpay/package.json +1 -1
  27. package/packages/apps/google-analytics/package.json +1 -1
  28. package/packages/apps/jadlog/package.json +1 -1
  29. package/packages/apps/loyalty-points/package.json +1 -1
  30. package/packages/apps/mandae/package.json +1 -1
  31. package/packages/apps/melhor-envio/package.json +1 -1
  32. package/packages/apps/mercadopago/package.json +1 -1
  33. package/packages/apps/pagarme/package.json +1 -1
  34. package/packages/apps/pagarme-v5/package.json +1 -1
  35. package/packages/apps/paghiper/package.json +1 -1
  36. package/packages/apps/pix/package.json +1 -1
  37. package/packages/apps/tiny-erp/package.json +1 -1
  38. package/packages/apps/webhooks/package.json +1 -1
  39. package/packages/cli/ci/bunny-config-base.sh +2 -0
  40. package/packages/cli/package.json +2 -2
  41. package/packages/config/package.json +1 -1
  42. package/packages/emails/package.json +1 -1
  43. package/packages/eslint/package.json +3 -3
  44. package/packages/events/package.json +1 -1
  45. package/packages/feeds/package.json +1 -1
  46. package/packages/firebase/package.json +3 -3
  47. package/packages/i18n/package.json +1 -1
  48. package/packages/modules/package.json +1 -1
  49. package/packages/passport/package.json +1 -1
  50. package/packages/ssr/lib/lib/serve-storefront.js +1 -1
  51. package/packages/ssr/lib/lib/serve-storefront.js.map +1 -1
  52. package/packages/ssr/package.json +3 -3
  53. package/packages/ssr/src/lib/serve-storefront.ts +2 -2
  54. package/packages/storefront/astro.config.mjs +5 -1
  55. package/packages/storefront/client.d.ts +3 -0
  56. package/packages/storefront/config/astro/client-sf-directive.mjs +97 -0
  57. package/packages/storefront/config/astro/index.d.ts +9 -2
  58. package/packages/storefront/config/storefront.cms.js +3 -3
  59. package/packages/storefront/package.json +8 -9
  60. package/packages/storefront/src/decap-cms/create-preview-component.ts +135 -0
  61. package/packages/storefront/src/decap-cms/get-cms-config.ts +320 -12
  62. package/packages/storefront/src/lib/assets/decap-cms.css +81 -0
  63. package/packages/storefront/src/lib/components/Drawer.vue +8 -13
  64. package/packages/storefront/src/lib/components/QuantitySelector.vue +4 -9
  65. package/packages/storefront/src/lib/composables/use-pitch-bar.ts +3 -9
  66. package/packages/storefront/src/lib/composables/use-product-shelf.ts +4 -1
  67. package/packages/storefront/src/lib/content.d.ts +19 -4
  68. package/packages/storefront/src/lib/layouts/BaseBody.astro +13 -0
  69. package/packages/storefront/src/lib/layouts/BaseHead.astro +1 -1
  70. package/packages/storefront/src/lib/scripts/decap-cms.ts +37 -29
  71. package/packages/storefront/src/lib/ssr-context.ts +35 -11
  72. package/packages/storefront/src/lib/state/use-cms-preview.ts +88 -27
  73. package/packages/test-base/package.json +1 -1
  74. package/packages/types/package.json +1 -1
  75. package/packages/storefront/config/astro/context-directive.mjs +0 -46
  76. package/packages/storefront/src/decap-cms/gen-preview-container.ts +0 -51
  77. package/packages/storefront/src/decap-cms/preview/webcontainer.ts +0 -104
@@ -1,27 +1,69 @@
1
+ import Deepmerge from '@fastify/deepmerge';
2
+ import { i18n as _i18n } from '@ecomplus/utils';
3
+
1
4
  export const getCmsConfig = () => {
2
- const baseDir = window.CMS_REPO_BASE_DIR || 'functions/ssr/';
5
+ const {
6
+ CMS_REPO_BASE_DIR,
7
+ CMS_LANG,
8
+ CMS_MAX_FILE_SIZE,
9
+ CMS,
10
+ } = window;
11
+ if (!CMS) throw Error('Missing global CMS');
12
+ const { domain } = window.$storefront?.settings || {};
13
+ const baseDir = CMS_REPO_BASE_DIR || 'functions/ssr/';
14
+ const locale = CMS_LANG || 'pt';
15
+ const i18n = (d: any) => _i18n(d, locale);
16
+ const deepmerge = Deepmerge();
17
+ CMS.registerLocale(locale, deepmerge(CMS.getLocale(locale), {
18
+ editor: {
19
+ editorControl: { field: { optional: '?' } },
20
+ editorWidgets: {
21
+ image: {
22
+ chooseDifferent: i18n({ en: 'Change image', pt: 'Mudar imagem' }),
23
+ remove: i18n({ en: 'Remove', pt: 'Remover' }),
24
+ },
25
+ },
26
+ },
27
+ }));
28
+ const maxFileSize = Math.max(CMS_MAX_FILE_SIZE || 0, 1000000);
3
29
  const config: Record<string, any> = {
30
+ locale,
4
31
  load_config_file: false,
5
- media_folder: `${baseDir}template/public/img/uploads`,
32
+ publish_mode: 'editorial_workflow',
33
+ media_folder: `${baseDir}public/img/uploads`,
6
34
  public_folder: '/img/uploads',
35
+ site_url: `https://${domain}/~preview`,
36
+ display_url: `https://${domain}`,
37
+ logo_url: 'https://ecom.nyc3.digitaloceanspaces.com/storefront/cms.png',
38
+ show_preview_links: true,
39
+ search: false,
7
40
  slug: {
8
41
  encoding: 'ascii',
9
42
  clean_accents: true,
10
43
  sanitize_replacement: '-',
11
44
  },
12
- collections: [
45
+ collections: i18n([
13
46
  {
14
- name: 'settings',
15
- label: 'Configurações',
16
- description: 'Configurações gerais para identidade e metadados do site',
47
+ name: 'config',
48
+ label: {
49
+ en: 'Settings',
50
+ pt: 'Configurações',
51
+ },
52
+ description: {
53
+ en: 'General settings for site identity and metadata',
54
+ pt: 'Configurações gerais para identidade e metadados do site',
55
+ },
17
56
  delete: false,
18
57
  editor: {
19
58
  preview: false,
20
59
  },
21
60
  files: [
22
61
  {
23
- name: 'general',
24
- label: 'Configurações gerais',
62
+ name: 'settings',
63
+ label: {
64
+ en: 'General config',
65
+ pt: 'Configurações gerais',
66
+ },
25
67
  file: `${baseDir}content/settings.json`,
26
68
  editor: {
27
69
  preview: true,
@@ -33,25 +75,291 @@ export const getCmsConfig = () => {
33
75
  widget: 'hidden',
34
76
  },
35
77
  {
36
- label: 'Repositório',
78
+ label: 'Repository',
37
79
  name: 'repository',
38
80
  widget: 'hidden',
39
81
  },
40
82
  {
41
- label: 'Domínio',
83
+ label: {
84
+ en: 'Domain name',
85
+ pt: 'Domínio',
86
+ },
42
87
  name: 'domain',
43
88
  widget: 'string',
44
89
  },
45
90
  {
46
- label: 'Nome da loja',
91
+ label: {
92
+ en: 'Store name',
93
+ pt: 'Nome da loja',
94
+ },
47
95
  name: 'name',
48
96
  widget: 'string',
49
97
  },
98
+ {
99
+ label: {
100
+ en: 'Short description',
101
+ pt: 'Descrição curta',
102
+ },
103
+ name: 'description',
104
+ widget: 'text',
105
+ },
106
+ {
107
+ label: {
108
+ en: 'Logo',
109
+ pt: 'Logomarca',
110
+ },
111
+ name: 'logo',
112
+ widget: 'image',
113
+ media_library: {
114
+ config: {
115
+ max_file_size: maxFileSize,
116
+ },
117
+ },
118
+ },
119
+ {
120
+ label: {
121
+ en: 'Icon',
122
+ pt: 'Ícone',
123
+ },
124
+ name: 'icon',
125
+ widget: 'image',
126
+ media_library: {
127
+ config: {
128
+ max_file_size: maxFileSize,
129
+ },
130
+ },
131
+ },
132
+ {
133
+ label: {
134
+ en: 'Primary color',
135
+ pt: 'Cor primária',
136
+ },
137
+ name: 'primaryColor',
138
+ widget: 'color',
139
+ },
140
+ {
141
+ label: {
142
+ en: 'Secondary color',
143
+ pt: 'Cor secundária',
144
+ },
145
+ name: 'secondaryColor',
146
+ widget: 'color',
147
+ required: false,
148
+ },
149
+ {
150
+ label: {
151
+ en: 'Contact email',
152
+ pt: 'E-mail de contato',
153
+ },
154
+ name: 'email',
155
+ widget: 'string',
156
+ required: false,
157
+ },
158
+ {
159
+ label: {
160
+ en: 'Service telephone',
161
+ pt: 'Telefone de atendimento',
162
+ },
163
+ name: 'phone',
164
+ widget: 'string',
165
+ required: false,
166
+ },
167
+ {
168
+ label: {
169
+ en: 'Store address',
170
+ pt: 'Endereço da loja',
171
+ },
172
+ name: 'address',
173
+ widget: 'string',
174
+ required: false,
175
+ },
176
+ {
177
+ label: {
178
+ en: 'Corporate name',
179
+ pt: 'Razão social da empresa',
180
+ },
181
+ name: 'corporateName',
182
+ widget: 'string',
183
+ },
184
+ {
185
+ label: {
186
+ en: 'Store document number',
187
+ pt: 'CNPJ',
188
+ },
189
+ name: 'docNumber',
190
+ widget: 'string',
191
+ },
192
+ {
193
+ label: {
194
+ en: 'Service links',
195
+ pt: 'Links de atendimento',
196
+ },
197
+ name: 'serviceLinks',
198
+ widget: 'list',
199
+ summary: '{{fields.title}}',
200
+ fields: [
201
+ {
202
+ label: {
203
+ en: 'Title',
204
+ pt: 'Título',
205
+ },
206
+ name: 'title',
207
+ widget: 'string',
208
+ },
209
+ {
210
+ label: 'Link',
211
+ name: 'href',
212
+ widget: 'string',
213
+ },
214
+ ],
215
+ },
216
+ {
217
+ label: {
218
+ en: 'Payment methods',
219
+ pt: 'Formas de pagamento',
220
+ },
221
+ name: 'paymentMethods',
222
+ widget: 'select',
223
+ multiple: true,
224
+ options: [
225
+ 'pix',
226
+ 'visa',
227
+ 'mastercard',
228
+ 'elo',
229
+ 'amex',
230
+ 'hipercard',
231
+ 'boleto',
232
+ 'diners',
233
+ 'discover',
234
+ ],
235
+ },
236
+ {
237
+ label: 'WhatsApp',
238
+ name: 'whatsapp',
239
+ widget: 'string',
240
+ required: false,
241
+ },
242
+ {
243
+ label: 'Instagram',
244
+ name: 'instagram',
245
+ widget: 'string',
246
+ required: false,
247
+ },
248
+ {
249
+ label: 'Facebook',
250
+ name: 'facebook',
251
+ widget: 'string',
252
+ required: false,
253
+ },
254
+ {
255
+ label: 'X / Twitter',
256
+ name: 'twitter',
257
+ widget: 'string',
258
+ required: false,
259
+ },
260
+ {
261
+ label: 'YouTube',
262
+ name: 'youtube',
263
+ widget: 'string',
264
+ required: false,
265
+ },
266
+ {
267
+ label: 'TikTok',
268
+ name: 'tiktok',
269
+ widget: 'string',
270
+ required: false,
271
+ },
272
+ {
273
+ label: 'Pinterest',
274
+ name: 'pinterest',
275
+ widget: 'string',
276
+ required: false,
277
+ },
278
+ {
279
+ label: 'Threads',
280
+ name: 'threads',
281
+ widget: 'string',
282
+ required: false,
283
+ },
284
+ {
285
+ label: {
286
+ en: 'Default locale',
287
+ pt: 'Língua padrão',
288
+ },
289
+ name: 'lang',
290
+ widget: 'select',
291
+ options: [
292
+ {
293
+ label: 'Português',
294
+ value: 'pt_br',
295
+ },
296
+ {
297
+ label: 'Inglês',
298
+ value: 'en_us',
299
+ },
300
+ ],
301
+ default: 'pt_br',
302
+ },
303
+ {
304
+ label: {
305
+ en: 'Currency code',
306
+ pt: 'Código da moeda',
307
+ },
308
+ name: 'currency',
309
+ widget: 'hidden',
310
+ default: 'BRL',
311
+ },
312
+ {
313
+ label: {
314
+ en: 'Currency symbol',
315
+ pt: 'Símbolo da moeda',
316
+ },
317
+ name: 'currencySymbol',
318
+ widget: 'hidden',
319
+ default: 'R$',
320
+ },
321
+ {
322
+ label: {
323
+ en: 'Country code',
324
+ pt: 'Código do país',
325
+ },
326
+ name: 'countryCode',
327
+ widget: 'hidden',
328
+ default: 'BR',
329
+ },
330
+ {
331
+ name: 'modules',
332
+ widget: 'hidden',
333
+ },
334
+ {
335
+ name: 'cartUrl',
336
+ widget: 'hidden',
337
+ },
338
+ {
339
+ name: 'checkoutUrl',
340
+ widget: 'hidden',
341
+ },
342
+ {
343
+ name: 'accountUrl',
344
+ widget: 'hidden',
345
+ },
346
+ {
347
+ name: 'ordersUrl',
348
+ widget: 'hidden',
349
+ },
350
+ {
351
+ name: 'favoritesUrl',
352
+ widget: 'hidden',
353
+ },
354
+ {
355
+ name: 'metafields',
356
+ widget: 'hidden',
357
+ },
50
358
  ],
51
359
  },
52
360
  ],
53
361
  },
54
- ],
362
+ ]),
55
363
  };
56
364
  return config;
57
365
  };
@@ -0,0 +1,81 @@
1
+ body {
2
+ [class*="FieldLabel"] {
3
+ font-size: 13px;
4
+ text-transform: none;
5
+ font-weight: 500;
6
+ width: calc(100% - 30px);
7
+ ~ input,
8
+ ~ textarea {
9
+ padding: 12px 14px;
10
+ border-width: 1px;
11
+ }
12
+ ~ [class*="ColorSwatch"] ~ input {
13
+ padding: 16px 70px;
14
+ }
15
+ ~ div[class*="83wr9v"] {
16
+ padding: 14px;
17
+ }
18
+ ~ div:not(:has([class*="TopBarContainer"])) {
19
+ &:has([class$="-control"]) {
20
+ border-width: 1px;
21
+ [class$="-control"] {
22
+ padding-left: 4px;
23
+ }
24
+ }
25
+ &:has([class*="ImageWrapper"]) {
26
+ border-width: 1px;
27
+ }
28
+ }
29
+ }
30
+ [class*="1qu85o2-FieldLabel"] {
31
+ background: none;
32
+ }
33
+ [class*="TopBarContainer"] {
34
+ [class*="ExpandButtonContainer"] {
35
+ display: none;
36
+ }
37
+ }
38
+ [class*="ControlContainer"]:has([class*="TopBarContainer"]) {
39
+ > [class*="FieldLabel"] {
40
+ display: none;
41
+ }
42
+ > div {
43
+ border-radius: 5px;
44
+ }
45
+ }
46
+ [class*="listControlItem"] {
47
+ [class*="ControlContainer"]:first-of-type {
48
+ margin-top: 12px;
49
+ }
50
+ [class*="DragIconContainer"] [class*="IconWrapper"] {
51
+ width: 26px;
52
+ height: 26px;
53
+ }
54
+ }
55
+ [class*="StyledListItemTopBar"] {
56
+ background-color: rgba(223 223 227 / .6);
57
+ ~ div {
58
+ border-color: rgba(223 223 227 / .6);
59
+ border-width: 1px;
60
+ }
61
+ }
62
+ [class*="NestedObjectLabel"] {
63
+ background-color: rgba(223 223 227 / .2);
64
+ }
65
+ [class*="ControlContainer"] {
66
+ [class*="ImageWrapper"] {
67
+ width: 100%;
68
+ margin: 0 0 12px;
69
+ }
70
+ [class*="FileWidgetButton"] {
71
+ display: inline-block;
72
+ margin: 0 12px 0 0;
73
+ }
74
+ [class*="FileWidgetButton-button"]:not(:first-of-type) {
75
+ display: none;
76
+ }
77
+ }
78
+ .Pane.Pane1 {
79
+ max-width: 600px;
80
+ }
81
+ }
@@ -1,6 +1,5 @@
1
1
  <script setup lang="ts">
2
2
  import {
3
- toRef,
4
3
  ref,
5
4
  computed,
6
5
  watch,
@@ -8,7 +7,6 @@ import {
8
7
  } from 'vue';
9
8
 
10
9
  export interface Props {
11
- modelValue?: boolean;
12
10
  isHidden?: boolean;
13
11
  placement?: 'start' | 'end' | 'top' | 'bottom';
14
12
  position?: 'fixed' | 'absolute';
@@ -22,7 +20,6 @@ export interface Props {
22
20
  }
23
21
 
24
22
  const props = withDefaults(defineProps<Props>(), {
25
- modelValue: false,
26
23
  isHidden: false,
27
24
  placement: 'start',
28
25
  position: 'fixed',
@@ -31,10 +28,8 @@ const props = withDefaults(defineProps<Props>(), {
31
28
  backdropTarget: '#teleported-top',
32
29
  canLockScroll: true,
33
30
  });
34
- const emit = defineEmits<{
35
- 'update:modelValue': [value: boolean]
36
- }>();
37
- const close = () => emit('update:modelValue', false);
31
+ const model = defineModel<boolean>({ default: false });
32
+ const close = () => { model.value = false; };
38
33
  const drawer = ref<HTMLElement | null>(null);
39
34
  const outsideClickListener = (ev: MouseEvent) => {
40
35
  if (!drawer.value?.contains(ev.target as Node)) {
@@ -48,7 +43,7 @@ const escClickListener = (ev: KeyboardEvent) => {
48
43
  }
49
44
  };
50
45
  const scrollbarWidth = ref(0);
51
- watch(toRef(props, 'modelValue'), async (isOpen) => {
46
+ watch(model, async (isOpen) => {
52
47
  if (isOpen) {
53
48
  if (props.canLockScroll) {
54
49
  scrollbarWidth.value = window.innerWidth - document.documentElement.clientWidth;
@@ -79,7 +74,7 @@ const slideTo = computed(() => {
79
74
  });
80
75
  const animationClassName = ref<string | null>(null);
81
76
  if (props.animation === 'scale') {
82
- watch(toRef(props, 'modelValue'), (isShown) => {
77
+ watch(model, (isShown) => {
83
78
  if (!isShown) {
84
79
  animationClassName.value = 'transition scale-90';
85
80
  } else {
@@ -87,7 +82,7 @@ if (props.animation === 'scale') {
87
82
  setTimeout(() => {
88
83
  animationClassName.value = 'transition';
89
84
  setTimeout(() => {
90
- if (props.modelValue) animationClassName.value = '';
85
+ if (model.value) animationClassName.value = '';
91
86
  }, 300);
92
87
  }, 50);
93
88
  });
@@ -109,7 +104,7 @@ const isPlacementX = computed(() => {
109
104
  <template>
110
105
  <Fade :slide="slideTo" speed="slow" is-floating>
111
106
  <dialog
112
- v-if="modelValue"
107
+ v-if="model"
113
108
  v-show="!isHidden"
114
109
  ref="drawer"
115
110
  class="z-40 w-screen p-0"
@@ -129,7 +124,7 @@ const isPlacementX = computed(() => {
129
124
  : !isPlacementX
130
125
  ? `calc(100vw - ${scrollbarWidth}px)` : undefined,
131
126
  }"
132
- :open="modelValue"
127
+ :open="model"
133
128
  :data-drawer="placement"
134
129
  >
135
130
  <div class="relative h-full">
@@ -151,7 +146,7 @@ const isPlacementX = computed(() => {
151
146
  <Teleport v-if="backdropTarget" :to="backdropTarget">
152
147
  <Fade>
153
148
  <div
154
- v-if="modelValue && !isHidden"
149
+ v-if="model && !isHidden"
155
150
  class="size-screen fixed left-0 top-0 z-30 bg-black/50"
156
151
  data-drawer-backdrop
157
152
  ></div>
@@ -22,7 +22,6 @@ export const quantitySelectorKey = Symbol('quantitySelector') as
22
22
 
23
23
  <script setup lang="ts">
24
24
  export interface Props {
25
- modelValue?: number;
26
25
  min?: number;
27
26
  max?: number;
28
27
  step?: number;
@@ -31,28 +30,24 @@ export interface Props {
31
30
  }
32
31
 
33
32
  const props = withDefaults(defineProps<Props>(), {
34
- modelValue: 1,
35
33
  min: 1,
36
34
  });
37
- const emit = defineEmits<{
38
- 'update:modelValue': [value: number]
39
- }>();
35
+ const model = defineModel<number>({ default: 1 });
40
36
  const input = ref<HTMLInputElement | null>(null);
41
37
  const inputId = useId('NInput');
42
38
  const value = computed({
43
39
  get() {
44
- return props.modelValue;
40
+ return model.value;
45
41
  },
46
42
  set(_value) {
47
43
  if (_value < props.min) {
48
44
  _value = props.min;
49
45
  (input.value as HTMLInputElement).value = `${_value}`;
50
- }
51
- if (props.max && _value > props.max) {
46
+ } else if (props.max && _value > props.max) {
52
47
  _value = props.max;
53
48
  (input.value as HTMLInputElement).value = `${_value}`;
54
49
  }
55
- emit('update:modelValue', _value);
50
+ model.value = _value;
56
51
  },
57
52
  });
58
53
  const isBoundMin = computed(() => {
@@ -4,11 +4,7 @@ import { parseShippingPhrase } from '@@sf/state/modules-info';
4
4
  import { useCmsPreview } from '@@sf/state/use-cms-preview';
5
5
 
6
6
  export interface Props {
7
- slides: Array<{
8
- href?: string;
9
- target?: string;
10
- html: string;
11
- }>;
7
+ slides: LayoutContent['header']['pitchBar'];
12
8
  }
13
9
 
14
10
  const parseLayoutContent = (layoutContent: LayoutContent) => {
@@ -20,11 +16,9 @@ const parseLayoutContent = (layoutContent: LayoutContent) => {
20
16
  };
21
17
 
22
18
  const usePitchBar = (props: Props) => {
23
- const { liveContent } = useCmsPreview('layout');
19
+ const { liveContent } = useCmsPreview(['layout', 'header', 'pitchBar']);
24
20
  const parsedContents = computed(() => {
25
- const slides = liveContent.value
26
- ? parseLayoutContent(liveContent.value).slides
27
- : props.slides;
21
+ const slides = liveContent.value || props.slides;
28
22
  return slides.map(({ html }) => {
29
23
  return parseShippingPhrase(html).value
30
24
  .replace(/<\/?p>/g, '')
@@ -1,10 +1,12 @@
1
1
  import type { ResourceId, Collections, SearchItem } from '@cloudcommerce/types';
2
+ import type { SectionPreviewProps } from '@@sf/state/use-cms-preview';
2
3
  import { ref, shallowReactive } from 'vue';
3
4
  import api from '@cloudcommerce/api';
4
5
  import { inStock as checkInStock } from '@ecomplus/utils';
5
6
  import { i19relatedProducts } from '@@i18n';
7
+ import { useSectionPreview } from '@@sf/state/use-cms-preview';
6
8
 
7
- export interface Props {
9
+ export interface Props extends Partial<SectionPreviewProps> {
8
10
  collectionId?: ResourceId | null;
9
11
  searchQuery?: `&${string}` | '';
10
12
  sort?: '-sales' | '-created_at' | 'price' | '-price' | '-price_discount' | string;
@@ -24,6 +26,7 @@ const useProductShelf = (props: Props) => {
24
26
  let fetching: Promise<void> | null = null;
25
27
  const fetchError = ref<Error | null>(null);
26
28
  const products = shallowReactive<SearchItem[]>(props.products || []);
29
+ useSectionPreview(props, { title, titleLink, products });
27
30
 
28
31
  if (!props.products) {
29
32
  isFetching.value = true;
@@ -5,14 +5,29 @@ export type SettingsContent = _SettingsContent &
5
5
 
6
6
  type _LayoutContent = typeof import('content/layout.json');
7
7
 
8
+ type _PitchBarSlide = {
9
+ href?: string;
10
+ target?: string;
11
+ html: string;
12
+ };
13
+
8
14
  export type LayoutContent = Omit<_LayoutContent, 'header' | 'footer'> & {
9
- header: Record<string, unknown> & _LayoutContent['header'],
10
- footer: Record<string, unknown> & Partial<_LayoutContent['footer']>,
15
+ header: { custom?: Record<string, unknown> } &
16
+ Omit<_LayoutContent['header'], 'pitchBar'> &
17
+ {
18
+ pitchBar: Array<
19
+ _LayoutContent['header']['pitchBar'][0] extends undefined
20
+ ? _PitchBarSlide
21
+ : Partial<_LayoutContent['header']['pitchBar'][0]> & _PitchBarSlide
22
+ >,
23
+ },
24
+ footer: { custom?: Record<string, unknown> } & Partial<_LayoutContent['footer']>,
25
+ custom?: Record<string, unknown>,
11
26
  };
12
27
 
13
28
  export interface PageContent {
14
- meta_title?: string;
15
- meta_description?: string;
29
+ metaTitle?: string;
30
+ metaDescription?: string;
16
31
  hero?: {
17
32
  [k: string]: unknown,
18
33
  autoplay?: number,
@@ -1,4 +1,7 @@
1
1
  ---
2
+ import * as url from 'node:url';
3
+ import { join as joinPath } from 'node:path';
4
+ import { readFileSync } from 'node:fs';
2
5
  import '@@sf/assets/reset.css';
3
6
  import '@@sf/assets/base.css';
4
7
  import '@@sf/assets/forms.css';
@@ -7,6 +10,15 @@ import '@@sf/assets/tooltip.css';
7
10
  // import 'uno.css';
8
11
  import Picture from '@@sf/components/Picture.astro';
9
12
 
13
+ const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
14
+ const pkgJson = readFileSync(joinPath(__dirname, '../../../package.json'), 'utf-8');
15
+ const {
16
+ name: pkgName,
17
+ version: pkgVersion,
18
+ } = JSON.parse(pkgJson);
19
+ const getInlineVersionLog = () => {
20
+ return `console.log('%c${pkgName} v${pkgVersion}', 'font-weight:bold')`;
21
+ };
10
22
  const { settings } = Astro.locals.routeContext;
11
23
  ---
12
24
 
@@ -28,4 +40,5 @@ const { settings } = Astro.locals.routeContext;
28
40
  hasImg={false}
29
41
  />
30
42
  }
43
+ <script is:inline set:html={getInlineVersionLog()} />
31
44
  </body>