cloudcommerce 0.28.0 → 0.28.1

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 (72) hide show
  1. package/.github/workflows/test-apps.yml +2 -2
  2. package/CHANGELOG.md +17 -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 +20 -3
  62. package/packages/storefront/config/astro/mock-pwa-info.mjs +3 -0
  63. package/packages/storefront/package.json +4 -4
  64. package/packages/storefront/src/lib/assets/tooltip.css +1 -0
  65. package/packages/storefront/src/lib/components/AccountLink.vue +5 -5
  66. package/packages/storefront/src/lib/components/CheckoutLink.vue +35 -4
  67. package/packages/storefront/src/lib/composables/use-sku-selector.ts +151 -0
  68. package/packages/storefront/src/lib/layouts/BaseHead.astro +11 -7
  69. package/packages/storefront/src/lib/ssr-context.ts +11 -4
  70. package/packages/test-base/package.json +1 -1
  71. package/packages/types/index.ts +1 -0
  72. 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
  }
@@ -22,6 +22,7 @@ const deployRand = process.env.DEPLOY_RAND || '_';
22
22
  const isLibDev = !(relativePath(__dirname, process.cwd()));
23
23
 
24
24
  const {
25
+ storeId,
25
26
  lang,
26
27
  domain,
27
28
  primaryColor,
@@ -36,9 +37,9 @@ const getIconUrl = (size) => {
36
37
  const _vitePWAOptions = {
37
38
  manifest: {
38
39
  name: settings.name || 'My Shop',
39
- short_name: settings.short_name || settings.name || 'MyShop',
40
+ short_name: settings.shortName || settings.name || 'MyShop',
40
41
  description: settings.description || 'My PWA Shop',
41
- background_color: settings.bg_color || '#f5f6fa',
42
+ background_color: settings.bgColor || '#f5f6fa',
42
43
  theme_color: primaryColor,
43
44
  crossorigin: 'use-credentials',
44
45
  icons: [{
@@ -165,6 +166,7 @@ viteAlias.push(
165
166
 
166
167
  const genAstroConfig = ({
167
168
  site = `https://${domain}`,
169
+ isPWA = false,
168
170
  vitePWAOptions = _vitePWAOptions,
169
171
  } = {}) => {
170
172
  const integrations = [
@@ -180,7 +182,6 @@ const genAstroConfig = ({
180
182
  injectReset: false,
181
183
  injectEntry: false,
182
184
  }),
183
- AstroPWA(vitePWAOptions),
184
185
  AutoImport({
185
186
  include: [
186
187
  /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
@@ -203,11 +204,25 @@ const genAstroConfig = ({
203
204
  },
204
205
  },
205
206
  ];
207
+ if (isPWA && !isSSG) {
208
+ integrations.push(AstroPWA(vitePWAOptions));
209
+ } else {
210
+ viteAlias.push({
211
+ find: 'virtual:pwa-info',
212
+ replacement: joinPath(__dirname, 'config/astro/mock-pwa-info.mjs'),
213
+ });
214
+ }
206
215
  if (!isToServerless) {
207
216
  integrations.push(image({
208
217
  serviceEntryPoint: '@astrojs/image/sharp',
209
218
  }));
210
219
  }
220
+ let assetsPrefix;
221
+ if (!isSSG) {
222
+ assetsPrefix = storeId === 1011 && domain === 'demo.ecomplus.app'
223
+ ? 'https://s2-demo.b-cdn.net'
224
+ : settings.assetsPrefix;
225
+ }
211
226
  return {
212
227
  experimental: {
213
228
  viewTransitions: true,
@@ -221,7 +236,9 @@ const genAstroConfig = ({
221
236
  site,
222
237
  compressHTML: isToServerless,
223
238
  build: {
239
+ assetsPrefix,
224
240
  inlineStylesheets: 'never',
241
+ ...settings.build,
225
242
  },
226
243
  vite: {
227
244
  plugins: [
@@ -0,0 +1,3 @@
1
+ /* eslint-disable */
2
+
3
+ export const pwaInfo = false;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/storefront",
3
3
  "type": "module",
4
- "version": "0.28.0",
4
+ "version": "0.28.1",
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.1",
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.1",
5
5
  "description": "E-Com Plus Cloud Commerce reusable type definitions",
6
6
  "main": "index.ts",
7
7
  "repository": {