cloudcommerce 0.28.0 → 0.28.2

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 (73) hide show
  1. package/.github/workflows/test-apps.yml +2 -2
  2. package/CHANGELOG.md +19 -0
  3. package/action.yml +2 -2
  4. package/ecomplus-stores/barra-doce/functions/many/package.json +3 -3
  5. package/ecomplus-stores/barra-doce/functions/ssr/package.json +6 -6
  6. package/ecomplus-stores/barra-doce/functions/with-apps/package.json +3 -3
  7. package/ecomplus-stores/barra-doce/package.json +2 -2
  8. package/ecomplus-stores/monocard/functions/many/package.json +3 -3
  9. package/ecomplus-stores/monocard/functions/ssr/package.json +6 -6
  10. package/ecomplus-stores/monocard/functions/with-apps/package.json +3 -3
  11. package/ecomplus-stores/monocard/package.json +2 -2
  12. package/package.json +6 -6
  13. package/packages/api/package.json +1 -1
  14. package/packages/apps/affilate-program/package.json +1 -1
  15. package/packages/apps/correios/package.json +1 -1
  16. package/packages/apps/custom-payment/package.json +1 -1
  17. package/packages/apps/custom-shipping/package.json +1 -1
  18. package/packages/apps/datafrete/package.json +1 -1
  19. package/packages/apps/discounts/package.json +1 -1
  20. package/packages/apps/emails/package.json +1 -1
  21. package/packages/apps/fb-conversions/package.json +1 -1
  22. package/packages/apps/flash-courier/package.json +1 -1
  23. package/packages/apps/frenet/package.json +1 -1
  24. package/packages/apps/galaxpay/package.json +1 -1
  25. package/packages/apps/google-analytics/package.json +1 -1
  26. package/packages/apps/jadlog/package.json +1 -1
  27. package/packages/apps/loyalty-points/package.json +1 -1
  28. package/packages/apps/melhor-envio/package.json +1 -1
  29. package/packages/apps/mercadopago/package.json +1 -1
  30. package/packages/apps/pagarme/package.json +1 -1
  31. package/packages/apps/paghiper/package.json +1 -1
  32. package/packages/apps/pix/package.json +1 -1
  33. package/packages/apps/tiny-erp/package.json +1 -1
  34. package/packages/apps/webhooks/package.json +1 -1
  35. package/packages/cli/package.json +1 -1
  36. package/packages/config/package.json +1 -1
  37. package/packages/emails/package.json +2 -2
  38. package/packages/eslint/package.json +4 -4
  39. package/packages/events/package.json +1 -1
  40. package/packages/feeds/package.json +1 -1
  41. package/packages/firebase/package.json +1 -1
  42. package/packages/i18n/lib/en_us/i19chooseProductDetailsToBuy.txt +1 -0
  43. package/packages/i18n/lib/en_us.d.ts +1 -0
  44. package/packages/i18n/lib/en_us.js +1 -0
  45. package/packages/i18n/lib/en_us.js.map +1 -1
  46. package/packages/i18n/lib/pt_br/i19chooseProductDetailsToBuy.txt +1 -0
  47. package/packages/i18n/lib/pt_br.d.ts +1 -0
  48. package/packages/i18n/lib/pt_br.js +1 -0
  49. package/packages/i18n/lib/pt_br.js.map +1 -1
  50. package/packages/i18n/package.json +1 -1
  51. package/packages/i18n/src/en_us.ts +1 -0
  52. package/packages/i18n/src/pt_br.ts +1 -0
  53. package/packages/modules/package.json +1 -1
  54. package/packages/passport/package.json +1 -1
  55. package/packages/ssr/cloudflare/swr-worker.js +2 -1
  56. package/packages/ssr/lib/firebase/serve-storefront.js +1 -1
  57. package/packages/ssr/lib/firebase/serve-storefront.js.map +1 -1
  58. package/packages/ssr/package.json +3 -3
  59. package/packages/ssr/src/cloudflare/swr-worker.ts +2 -1
  60. package/packages/ssr/src/firebase/serve-storefront.ts +1 -1
  61. package/packages/storefront/astro.config.mjs +13 -3
  62. package/packages/storefront/config/astro/mock-pwa-info.mjs +3 -0
  63. package/packages/storefront/config/storefront.config.mjs +4 -0
  64. package/packages/storefront/package.json +4 -4
  65. package/packages/storefront/src/lib/assets/tooltip.css +1 -0
  66. package/packages/storefront/src/lib/components/AccountLink.vue +5 -5
  67. package/packages/storefront/src/lib/components/CheckoutLink.vue +35 -4
  68. package/packages/storefront/src/lib/composables/use-sku-selector.ts +151 -0
  69. package/packages/storefront/src/lib/layouts/BaseHead.astro +11 -7
  70. package/packages/storefront/src/lib/ssr-context.ts +11 -4
  71. package/packages/test-base/package.json +1 -1
  72. package/packages/types/index.ts +1 -0
  73. package/packages/types/package.json +1 -1
@@ -88,7 +88,8 @@ export type Env = Record<`OVERRIDE_${string}`, string | undefined> & {
88
88
  PERMA_CACHE: KVNamespace | undefined;
89
89
  };
90
90
 
91
- const swr = async (_request: Request, env: Env, ctx: ExecutionContext) => {
91
+ const swr = async (_rewritedReq: Request, env: Env, ctx: ExecutionContext) => {
92
+ const _request = new Request(_rewritedReq.url.replace('/__swr/', '/'), _rewritedReq);
92
93
  const url = new URL(_request.url);
93
94
  const { hostname, pathname } = url;
94
95
  const hostOverride = env[`OVERRIDE_${hostname}`];
@@ -226,7 +226,7 @@ export default async (req: Request, res: Response) => {
226
226
  /* eslint-disable prefer-rest-params */
227
227
  // @ts-ignore
228
228
  res.writeHead = function writeHead(status: number, headers: OutgoingHttpHeaders) {
229
- if (status === 200 && headers && cssFilepath) {
229
+ if (status === 200 && headers && cssFilepath && !headers.link) {
230
230
  // https://community.cloudflare.com/t/early-hints-need-more-data-before-switching-over/342888/21
231
231
  headers.Link = `<${cssFilepath}>; rel=preload; as=style`;
232
232
  }
@@ -36,9 +36,9 @@ const getIconUrl = (size) => {
36
36
  const _vitePWAOptions = {
37
37
  manifest: {
38
38
  name: settings.name || 'My Shop',
39
- short_name: settings.short_name || settings.name || 'MyShop',
39
+ short_name: settings.shortName || settings.name || 'MyShop',
40
40
  description: settings.description || 'My PWA Shop',
41
- background_color: settings.bg_color || '#f5f6fa',
41
+ background_color: settings.bgColor || '#f5f6fa',
42
42
  theme_color: primaryColor,
43
43
  crossorigin: 'use-credentials',
44
44
  icons: [{
@@ -165,6 +165,7 @@ viteAlias.push(
165
165
 
166
166
  const genAstroConfig = ({
167
167
  site = `https://${domain}`,
168
+ isPWA = false,
168
169
  vitePWAOptions = _vitePWAOptions,
169
170
  } = {}) => {
170
171
  const integrations = [
@@ -180,7 +181,6 @@ const genAstroConfig = ({
180
181
  injectReset: false,
181
182
  injectEntry: false,
182
183
  }),
183
- AstroPWA(vitePWAOptions),
184
184
  AutoImport({
185
185
  include: [
186
186
  /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
@@ -203,6 +203,14 @@ const genAstroConfig = ({
203
203
  },
204
204
  },
205
205
  ];
206
+ if (isPWA && !isSSG) {
207
+ integrations.push(AstroPWA(vitePWAOptions));
208
+ } else {
209
+ viteAlias.push({
210
+ find: 'virtual:pwa-info',
211
+ replacement: joinPath(__dirname, 'config/astro/mock-pwa-info.mjs'),
212
+ });
213
+ }
206
214
  if (!isToServerless) {
207
215
  integrations.push(image({
208
216
  serviceEntryPoint: '@astrojs/image/sharp',
@@ -221,7 +229,9 @@ const genAstroConfig = ({
221
229
  site,
222
230
  compressHTML: isToServerless,
223
231
  build: {
232
+ assetsPrefix: !isSSG ? settings.assetsPrefix : undefined,
224
233
  inlineStylesheets: 'never',
234
+ ...settings.build,
225
235
  },
226
236
  vite: {
227
237
  plugins: [
@@ -0,0 +1,3 @@
1
+ /* eslint-disable */
2
+
3
+ export const pwaInfo = false;
@@ -50,6 +50,10 @@ export default () => {
50
50
  currencySymbol,
51
51
  } = config.get();
52
52
 
53
+ if (!settings.assetsPrefix && storeId === 1011 && domain === 'demo.ecomplus.app') {
54
+ settings.assetsPrefix = 'https://s2-demo.b-cdn.net';
55
+ }
56
+
53
57
  return {
54
58
  storeId,
55
59
  lang,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/storefront",
3
3
  "type": "module",
4
- "version": "0.28.0",
4
+ "version": "0.28.2",
5
5
  "description": "E-Com Plus Cloud Commerce storefront with Astro",
6
6
  "bin": {
7
7
  "storefront": "./scripts/build-prod.sh"
@@ -42,7 +42,7 @@
42
42
  "@iconify-json/heroicons": "^1.1.12",
43
43
  "@iconify-json/logos": "^1.1.37",
44
44
  "@vite-pwa/astro": "^0.1.3",
45
- "@vueuse/core": "10.4.1",
45
+ "@vueuse/core": "10.5.0",
46
46
  "astro": "2.10.15",
47
47
  "astro-capo": "^0.0.1",
48
48
  "chroma-js": "^2.4.2",
@@ -53,9 +53,9 @@
53
53
  "semver": "^7.5.4",
54
54
  "sharp": "^0.32.6",
55
55
  "tailwindcss": "^3.3.3",
56
- "unocss": "^0.56.4",
56
+ "unocss": "^0.56.5",
57
57
  "unplugin-auto-import": "^0.16.6",
58
- "vite": "^4.4.9",
58
+ "vite": "^4.4.11",
59
59
  "vite-plugin-pwa": "^0.16.5",
60
60
  "vue": "^3.3.4"
61
61
  },
@@ -16,6 +16,7 @@
16
16
  top: 102%;
17
17
  margin-top: 5px;
18
18
  font-size: clamp(0.75rem, 90%, 1rem);
19
+ font-weight: 400;
19
20
  width: max-content;
20
21
  background-color: var(--background-color);
21
22
  color: var(--color);
@@ -9,7 +9,7 @@ export interface Props {
9
9
  }
10
10
 
11
11
  const props = withDefaults(defineProps<Props>(), {
12
- accountUrl: globalThis.$storefront.settings.account_url || '/app/account',
12
+ accountUrl: globalThis.$storefront.settings.accountUrl || '/app/account',
13
13
  returnUrl: globalThis.location?.href,
14
14
  });
15
15
  const href = computed(() => {
@@ -23,11 +23,11 @@ const href = computed(() => {
23
23
  return `${url}return_url=${props.returnUrl}`;
24
24
  }
25
25
  const { settings } = globalThis.$storefront;
26
- if (props.to === 'orders' && settings.orders_url) {
27
- return settings.orders_url;
26
+ if (props.to === 'orders' && settings.ordersUrl) {
27
+ return settings.ordersUrl;
28
28
  }
29
- if (props.to === 'favorites' && settings.favorites_url) {
30
- return settings.favorites_url;
29
+ if (props.to === 'favorites' && settings.favoritesUrl) {
30
+ return settings.favoritesUrl;
31
31
  }
32
32
  return `${url}/${props.to}`;
33
33
  });
@@ -1,20 +1,51 @@
1
1
  <script setup lang="ts">
2
+ import type { CartItem } from '@@sf/composables/use-cart-item';
2
3
  import { computed } from 'vue';
3
4
 
4
5
  export interface Props {
5
6
  to?: 'cart' | 'checkout';
6
7
  cartUrl?: string;
7
8
  checkoutUrl?: string;
9
+ cartId?: CartItem['_id'];
10
+ cartItem?: Partial<CartItem> & { product_id: CartItem['product_id'] };
8
11
  }
9
12
 
10
13
  const props = withDefaults(defineProps<Props>(), {
11
14
  to: 'cart',
12
- cartUrl: globalThis.$storefront.settings.cart_url || '/app/#/cart',
13
- checkoutUrl: globalThis.$storefront.settings.checkout_url || '/app/#/checkout',
15
+ cartUrl: globalThis.$storefront.settings.cartUrl || '/app/#/cart',
16
+ checkoutUrl: globalThis.$storefront.settings.checkoutUrl || '/app/#/checkout',
14
17
  });
15
18
  const href = computed(() => {
16
- if (props.to === 'cart') return props.cartUrl;
17
- return props.checkoutUrl;
19
+ let url = (props.to === 'cart') ? props.cartUrl : props.checkoutUrl;
20
+ if (props.cartId) {
21
+ url += `/${props.cartId}`;
22
+ } else if (props.cartItem) {
23
+ const params: Record<string, string> = {
24
+ product_id: props.cartItem.product_id,
25
+ };
26
+ const {
27
+ variation_id: variationId,
28
+ quantity,
29
+ sku,
30
+ customizations,
31
+ } = props.cartItem;
32
+ if (variationId) params.variation_id = variationId;
33
+ if (quantity) params.quantity = String(quantity);
34
+ if (sku) params.sku = sku;
35
+ if (customizations) {
36
+ customizations.forEach(({ label, option, attachment }, i) => {
37
+ const prefix = `customizations.${i}.`;
38
+ if (label) params[prefix + 'label'] = label;
39
+ if (option) {
40
+ if (option._id) params[prefix + 'option._id'] = option._id;
41
+ params[prefix + 'option.text'] = option.text;
42
+ }
43
+ if (attachment) params[prefix + 'attachment'] = attachment;
44
+ });
45
+ }
46
+ url += '?' + new URLSearchParams(params).toString();
47
+ }
48
+ return url;
18
49
  });
19
50
  </script>
20
51
 
@@ -0,0 +1,151 @@
1
+ import type {
2
+ ResourceId,
3
+ Products,
4
+ GridsList,
5
+ } from '@cloudcommerce/api/types';
6
+ import {
7
+ ref,
8
+ computed,
9
+ reactive,
10
+ shallowReactive,
11
+ watch,
12
+ toRef,
13
+ } from 'vue';
14
+ import api from '@cloudcommerce/api';
15
+ import {
16
+ specValueByText as getSpecValueByText,
17
+ specTextValue as getSpecTextValue,
18
+ variationsGrids as getVariationsGrids,
19
+ gridTitle as _getGridTitle,
20
+ } from '@ecomplus/utils';
21
+
22
+ export interface Props {
23
+ variations: Exclude<Products['variations'], undefined>;
24
+ variationId?: ResourceId | null;
25
+ }
26
+
27
+ const useSkuSelector = (props: Props) => {
28
+ let grids = shallowReactive<GridsList<null>>(globalThis.$storefront.data.grids);
29
+ if (!grids) {
30
+ grids = shallowReactive([]);
31
+ api.get('grids').then(({ data: { result } }) => {
32
+ result.forEach((grid) => grids.push(grid));
33
+ });
34
+ }
35
+ const variationsGrids = reactive<Record<string, string[]>>({});
36
+ const activeVariationsGrids = reactive<Record<string, string[]>>({});
37
+ watch(toRef(props, 'variations'), () => {
38
+ Object.assign(activeVariationsGrids, getVariationsGrids(props, undefined, true));
39
+ const allVariationsGrids = getVariationsGrids(props);
40
+ Object.keys(allVariationsGrids).forEach((gridId) => {
41
+ if (activeVariationsGrids[gridId]) {
42
+ variationsGrids[gridId] = activeVariationsGrids[gridId];
43
+ } else {
44
+ delete variationsGrids[gridId];
45
+ }
46
+ });
47
+ }, { immediate: true });
48
+
49
+ const orderedGridIds = computed(() => Object.keys(variationsGrids));
50
+ const selectedOptions = reactive<Record<string, string>>({});
51
+ const variationId = ref<ResourceId | null | undefined>();
52
+ const selectOption = ({ optionText, gridId, gridIndex }: {
53
+ optionText: string,
54
+ gridId: string,
55
+ gridIndex: number,
56
+ }) => {
57
+ selectedOptions[gridId] = optionText;
58
+ const filteredGrids = {};
59
+ for (let i = 0; i <= gridIndex; i++) {
60
+ const _gridId = orderedGridIds[i];
61
+ if (selectedOptions[_gridId]) {
62
+ filteredGrids[_gridId] = selectedOptions[_gridId];
63
+ }
64
+ }
65
+ const nextFilteredGrids = getVariationsGrids(props, filteredGrids, true);
66
+ for (let i = gridIndex + 1; i < orderedGridIds.value.length; i++) {
67
+ const _gridId = orderedGridIds[i];
68
+ const options = nextFilteredGrids[_gridId];
69
+ activeVariationsGrids[_gridId] = options;
70
+ if (selectedOptions[_gridId] && !options.includes(selectedOptions[_gridId])) {
71
+ delete selectedOptions[_gridId];
72
+ }
73
+ }
74
+ const variations = props.variations.slice(0);
75
+ for (let i = 0; i < variations.length; i++) {
76
+ const variation = variations[i];
77
+ const { specifications } = variation;
78
+ const gridIds = Object.keys(specifications);
79
+ for (let ii = 0; ii < gridIds.length; ii++) {
80
+ const _gridId = gridIds[ii];
81
+ // @ts-ignore
82
+ if (selectedOptions[_gridId] !== getSpecTextValue(variation, _gridId)) {
83
+ variations.splice(i, 1);
84
+ i -= 1;
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ variationId.value = variations[0]?._id || null;
90
+ };
91
+ watch(toRef(props, 'variationId'), (_variationId) => {
92
+ if (!_variationId || _variationId === variationId.value) {
93
+ return;
94
+ }
95
+ const selectedVariation = props.variations.find((variation) => {
96
+ return variation._id === _variationId;
97
+ });
98
+ if (!selectedVariation) {
99
+ variationId.value = null;
100
+ return;
101
+ }
102
+ const { specifications } = selectedVariation;
103
+ const specs = Object.keys(specifications);
104
+ const nextSpec = (specIndex = 0) => {
105
+ const gridId = specs[specIndex];
106
+ if (
107
+ specs[specIndex]
108
+ && specifications[gridId]
109
+ && specifications[gridId].length === 1
110
+ ) {
111
+ const optionText = specifications[gridId][0].text;
112
+ if (variationsGrids[gridId].find((option) => option === optionText)) {
113
+ selectOption({
114
+ optionText,
115
+ gridId,
116
+ gridIndex: orderedGridIds.value.indexOf(gridId),
117
+ });
118
+ nextSpec(specIndex + 1);
119
+ }
120
+ }
121
+ };
122
+ nextSpec();
123
+ }, { immediate: true });
124
+
125
+ const getGridTitle = (gridId: string) => {
126
+ return _getGridTitle(gridId, grids);
127
+ };
128
+ const getColorOptionBg = (optionText: string) => {
129
+ const rgbs = optionText.split(',').map((colorName) => {
130
+ return getSpecValueByText(props.variations, colorName.trim(), 'colors');
131
+ });
132
+ return rgbs.length > 1
133
+ ? `background:linear-gradient(to right bottom, ${rgbs[0]} 50%, ${rgbs[1]} 50%)`
134
+ : `background:${rgbs[0]}`;
135
+ };
136
+
137
+ return {
138
+ grids,
139
+ variationsGrids,
140
+ activeVariationsGrids,
141
+ selectOption,
142
+ selectedOptions,
143
+ variationId,
144
+ getGridTitle,
145
+ getColorOptionBg,
146
+ };
147
+ };
148
+
149
+ export default useSkuSelector;
150
+
151
+ export { useSkuSelector };
@@ -7,6 +7,8 @@ export interface Props {
7
7
  hasViewTransitions?: boolean;
8
8
  }
9
9
 
10
+ // @ts-ignore
11
+ const isPWA = pwaInfo !== false; // config/astro/mock-pwa-info.mjs
10
12
  const hasViewTransitions = Astro.props.hasViewTransitions !== false;
11
13
  const deployRand = import.meta.env.DEPLOY_RAND || '_';
12
14
  const getIconUrl = (size: number) => {
@@ -161,13 +163,15 @@ const inlineJSONLd = JSON.stringify({
161
163
  <meta name="twitter:site" content={metatagsContent.twitterUsername} />}
162
164
  <meta name="ecom-store-id" content={String(storeId)}>
163
165
 
164
- <script>
165
- import { registerSW } from 'virtual:pwa-register';
166
- registerSW({ immediate: false });
167
- </script>
168
- {pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
169
- {(!pwaInfo && !import.meta.env.DEV) &&
170
- <link rel="manifest" href="/manifest.webmanifest" />}
166
+ {isPWA && <>
167
+ <script>
168
+ import { registerSW } from 'virtual:pwa-register';
169
+ registerSW({ immediate: false });
170
+ </script>
171
+ {pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
172
+ {(!pwaInfo && !import.meta.env.DEV) &&
173
+ <link rel="manifest" href="/manifest.webmanifest" />}
174
+ </>}
171
175
 
172
176
  <script is:inline set:html={inlineClientJS} transition:persist />
173
177
  <script type="application/ld+json" set:html={inlineJSONLd} />
@@ -14,8 +14,8 @@ export type StorefrontConfig = {
14
14
  currency: BaseConfig['currency'],
15
15
  currencySymbol: BaseConfig['currencySymbol'],
16
16
  domain: SettingsContent['domain'],
17
- primaryColor: SettingsContent['primary_color'],
18
- secondaryColor: SettingsContent['secondary_color'],
17
+ primaryColor: SettingsContent['primaryColor'],
18
+ secondaryColor: SettingsContent['secondaryColor'],
19
19
  settings: SettingsContent,
20
20
  getContent: ContentGetter,
21
21
  };
@@ -179,12 +179,19 @@ const loadRouteContext = async (Astro: Readonly<AstroGlobal>, {
179
179
  throw err;
180
180
  }
181
181
  Astro.response.headers.set('X-Load-Took', String(Date.now() - startedAt));
182
+ if (import.meta.env.PROD) {
183
+ const { assetsPrefix } = config.settings;
184
+ if (assetsPrefix && assetsPrefix.startsWith('https://')) {
185
+ const cdnURL = assetsPrefix.replace(/(https:\/\/[^/]+).*/, '$1');
186
+ Astro.response.headers.set('Link', `<${cdnURL}/>; rel=preconnect`);
187
+ }
188
+ }
182
189
  if (urlPath === '/~fallback') {
183
190
  setResponseCache(Astro, 3600, 86400);
184
191
  } else if (isHomepage) {
185
- setResponseCache(Astro, 180, 300);
192
+ setResponseCache(Astro, 180);
186
193
  } else {
187
- setResponseCache(Astro, 120, 300);
194
+ setResponseCache(Astro, 120, 180);
188
195
  }
189
196
  const routeContext = {
190
197
  ...config,
@@ -2,7 +2,7 @@
2
2
  "name": "@cloudcommerce/test-base",
3
3
  "private": true,
4
4
  "type": "module",
5
- "version": "0.28.0",
5
+ "version": "0.28.2",
6
6
  "description": "E-Com Plus Cloud Commerce basic setup for testing",
7
7
  "main": "lib/index.js",
8
8
  "repository": {
@@ -100,6 +100,7 @@ type SettingsContent = {
100
100
  primaryColor: string,
101
101
  secondaryColor?: string,
102
102
  bgColor?: string,
103
+ assetsPrefix?: string,
103
104
  email: string,
104
105
  phone: string,
105
106
  address: string,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/types",
3
3
  "type": "module",
4
- "version": "0.28.0",
4
+ "version": "0.28.2",
5
5
  "description": "E-Com Plus Cloud Commerce reusable type definitions",
6
6
  "main": "index.ts",
7
7
  "repository": {