@rocketsearch/vue-uappi 0.1.1 → 0.1.3
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/README.md +3 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/runtime/components/RocketLanding.vue +60 -0
- package/runtime/components/RocketShowcase.vue +39 -7
- package/runtime/css/showcase.css +5 -0
- package/runtime/rocket.js +14 -7
package/README.md
CHANGED
|
@@ -27,4 +27,7 @@ optimizeDeps: { exclude: ['@rocketsearch/vue-uappi'] }
|
|
|
27
27
|
Use os componentes em PascalCase (`<RocketSearch>`, `<RocketShowcase position="top">`): o tema pode ter
|
|
28
28
|
`isCustomElement` para tags com hífen.
|
|
29
29
|
|
|
30
|
+
Landing page ("Personalizado" no painel): `<RocketLanding>` mostra todas as vitrines da landing, na ordem do
|
|
31
|
+
painel. O identificador vem do nome da rota (`/black-friday` → `black-friday`) ou de `custom-key="..."`.
|
|
32
|
+
|
|
30
33
|
Documentação completa: https://api.rocketsearch.com.br/sdk/uappi/docs.html
|
package/index.js
CHANGED
|
@@ -109,6 +109,7 @@ export default {
|
|
|
109
109
|
app.component('RocketBanner', defineAsyncComponent(() => import('./runtime/components/RocketBanner.vue')));
|
|
110
110
|
app.component('RocketSearch', defineAsyncComponent(() => import('./runtime/components/RocketSearch.vue')));
|
|
111
111
|
app.component('RocketShowcase', defineAsyncComponent(() => import('./runtime/components/RocketShowcase.vue')));
|
|
112
|
+
app.component('RocketLanding', defineAsyncComponent(() => import('./runtime/components/RocketLanding.vue')));
|
|
112
113
|
|
|
113
114
|
// Conexão fria + preflight CORS era o grosso da latência do storefront, não a busca.
|
|
114
115
|
const preconnect = document.createElement('link');
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<RocketShowcase
|
|
3
|
+
v-for="showcase in showcases"
|
|
4
|
+
:key="showcase.id"
|
|
5
|
+
page-type="custom"
|
|
6
|
+
:custom-key="key"
|
|
7
|
+
:showcase-id="showcase.id"
|
|
8
|
+
:position="(showcase.placement && showcase.placement.position) || 'middle'"
|
|
9
|
+
:media-size="mediaSize"
|
|
10
|
+
>
|
|
11
|
+
<template v-if="$slots.default" #default="scope">
|
|
12
|
+
<slot v-bind="scope" />
|
|
13
|
+
</template>
|
|
14
|
+
<template v-if="$slots.product" #product="scope">
|
|
15
|
+
<slot name="product" v-bind="scope" />
|
|
16
|
+
</template>
|
|
17
|
+
</RocketShowcase>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
import RocketShowcase from './RocketShowcase.vue';
|
|
22
|
+
|
|
23
|
+
// Landing page ("Personalizado" no painel): todas as vitrines da landing, empilhadas pela ordem
|
|
24
|
+
// do painel (a API já responde por display_order). Sem elemento próprio: as vitrines entram
|
|
25
|
+
// direto no container do tema e herdam o espaçamento dele.
|
|
26
|
+
export default {
|
|
27
|
+
name: 'RocketLanding',
|
|
28
|
+
components: { RocketShowcase },
|
|
29
|
+
inheritAttrs: false,
|
|
30
|
+
props: {
|
|
31
|
+
// Identificador da landing no painel; sem ele, o nome da rota (/black-friday -> black-friday).
|
|
32
|
+
customKey: { type: String, default: '' },
|
|
33
|
+
mediaSize: { type: String, default: 'medium' }
|
|
34
|
+
},
|
|
35
|
+
data () {
|
|
36
|
+
return { showcases: [] };
|
|
37
|
+
},
|
|
38
|
+
computed: {
|
|
39
|
+
key () {
|
|
40
|
+
if (this.customKey) return this.customKey;
|
|
41
|
+
const path = (this.$route && this.$route.path) || location.pathname;
|
|
42
|
+
return path.split('/').filter(Boolean).pop() || '';
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
watch: {
|
|
46
|
+
key: 'load'
|
|
47
|
+
},
|
|
48
|
+
mounted () {
|
|
49
|
+
this.load();
|
|
50
|
+
},
|
|
51
|
+
methods: {
|
|
52
|
+
async load () {
|
|
53
|
+
const key = this.key;
|
|
54
|
+
const showcases = key ? await this.$rocket.getPageShowcases('custom', key, true) : [];
|
|
55
|
+
// Troca rápida de rota: a resposta da landing anterior não sobrescreve a atual.
|
|
56
|
+
if (key === this.key) this.showcases = showcases;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
</script>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
v-if="showcase || loading || debug"
|
|
4
|
-
:class="['rocket-search', { 'rocket-search--empty': !showcase && !loading }]"
|
|
5
|
-
:data-rocket-showcase-position="position"
|
|
3
|
+
v-if="showcase || loading || debug || restIds.length"
|
|
4
|
+
:class="['rocket-search', { 'rocket-search--empty': !showcase && !loading && !restIds.length, 'rocket-search--stacked': stacked }]"
|
|
5
|
+
:data-rocket-showcase-position="stacked ? null : position"
|
|
6
6
|
>
|
|
7
7
|
<div v-if="loading" class="rocket-search__showcase rocket-search__showcase--skeleton" :style="columnsStyle">
|
|
8
8
|
<div class="rocket-search__showcase__title rocket-search__product-placeholder__item" />
|
|
@@ -47,6 +47,26 @@
|
|
|
47
47
|
<button type="button" class="rocket-search__swiper-button-next" aria-label="Próximos" @click="slide(1)" />
|
|
48
48
|
</slot>
|
|
49
49
|
</section>
|
|
50
|
+
|
|
51
|
+
<!-- Demais vitrines da mesma posição, na ordem da API: cada uma medida por conta própria. -->
|
|
52
|
+
<RocketShowcase
|
|
53
|
+
v-for="id in restIds"
|
|
54
|
+
:key="id"
|
|
55
|
+
:position="position"
|
|
56
|
+
:page-type="pageType"
|
|
57
|
+
:custom-key="customKey"
|
|
58
|
+
:showcase-id="id"
|
|
59
|
+
:media-size="mediaSize"
|
|
60
|
+
stacked
|
|
61
|
+
@products-loaded="$emit('products-loaded', $event)"
|
|
62
|
+
>
|
|
63
|
+
<template v-if="$slots.default" #default="scope">
|
|
64
|
+
<slot v-bind="scope" />
|
|
65
|
+
</template>
|
|
66
|
+
<template v-if="$slots.product" #product="scope">
|
|
67
|
+
<slot name="product" v-bind="scope" />
|
|
68
|
+
</template>
|
|
69
|
+
</RocketShowcase>
|
|
50
70
|
</div>
|
|
51
71
|
</template>
|
|
52
72
|
|
|
@@ -69,10 +89,17 @@ export default {
|
|
|
69
89
|
position: { type: String, required: true },
|
|
70
90
|
// Força o pageType; sem ele vem da página atual (currentPageData do tema).
|
|
71
91
|
pageType: { type: String, default: '' },
|
|
72
|
-
|
|
92
|
+
// Landing (page-type="custom"): identificador cadastrado no painel em "Personalizado".
|
|
93
|
+
customKey: { type: String, default: '' },
|
|
94
|
+
// Vitrine específica (a landing tem várias na mesma posição); sem ele, todas as da posição.
|
|
95
|
+
showcaseId: { type: [String, Number], default: null },
|
|
96
|
+
mediaSize: { type: String, default: 'medium' },
|
|
97
|
+
// Uso interno: vitrine empilhada abaixo da primeira da posição. Sem o atributo de posição,
|
|
98
|
+
// o debug contorna a posição uma vez só.
|
|
99
|
+
stacked: { type: Boolean, default: false }
|
|
73
100
|
},
|
|
74
101
|
data () {
|
|
75
|
-
return { showcase: null, products: [], loading: false, debug: false };
|
|
102
|
+
return { showcase: null, products: [], restIds: [], loading: false, debug: false };
|
|
76
103
|
},
|
|
77
104
|
computed: {
|
|
78
105
|
columns () {
|
|
@@ -117,9 +144,14 @@ export default {
|
|
|
117
144
|
if (this.started) return;
|
|
118
145
|
this.started = true;
|
|
119
146
|
try {
|
|
120
|
-
const showcases = await this.$rocket.getPageShowcases(this.pageType || undefined);
|
|
121
|
-
const
|
|
147
|
+
const showcases = await this.$rocket.getPageShowcases(this.pageType || undefined, this.customKey || undefined);
|
|
148
|
+
const inPosition = showcases.filter(s => ((s.placement && s.placement.position) || s.position) === this.position);
|
|
149
|
+
const showcase = this.showcaseId != null
|
|
150
|
+
? showcases.find(s => String(s.id) === String(this.showcaseId))
|
|
151
|
+
: inPosition[0];
|
|
122
152
|
if (!showcase) return;
|
|
153
|
+
// Posição sem showcaseId: este slot mostra a primeira e empilha as demais logo abaixo.
|
|
154
|
+
if (this.showcaseId == null) this.restIds = inPosition.slice(1).map(s => s.id);
|
|
123
155
|
// Banner autônomo: render direto, sem produtos, skeleton nem impressão de vitrine
|
|
124
156
|
// (o RocketBannerItem mede o criativo).
|
|
125
157
|
if (isBannerItem(showcase)) {
|
package/runtime/css/showcase.css
CHANGED
package/runtime/rocket.js
CHANGED
|
@@ -122,7 +122,10 @@ export function createRocket (config) {
|
|
|
122
122
|
|
|
123
123
|
rocket.setPage = function (next) {
|
|
124
124
|
page = Object.assign({ section: null, productId: null, categoryId: null, product: null }, next);
|
|
125
|
-
|
|
125
|
+
// A landing (custom) não depende do contexto da página e fica: senão, um setPage entre a busca
|
|
126
|
+
// do RocketLanding e a montagem das vitrines dele dispararia uma segunda chamada. Ela se
|
|
127
|
+
// renova quando o RocketLanding monta (refresh).
|
|
128
|
+
Object.keys(pageShowcases).forEach((k) => { if (k.indexOf('custom|') !== 0) delete pageShowcases[k]; });
|
|
126
129
|
};
|
|
127
130
|
rocket.getPage = () => page;
|
|
128
131
|
|
|
@@ -294,13 +297,15 @@ export function createRocket (config) {
|
|
|
294
297
|
return res.json();
|
|
295
298
|
};
|
|
296
299
|
|
|
297
|
-
|
|
300
|
+
// customKey: identificador da landing (pageType=custom); sem ele a API responde 400.
|
|
301
|
+
rocket.getShowcasesByContext = async function (type, customKey) {
|
|
298
302
|
const pageType = resolvePageType(type);
|
|
299
303
|
if (!pageType) return [];
|
|
300
304
|
const sessionId = await getSessionIdWithTimeout();
|
|
301
305
|
const params = { storeId: config.storeId, pageType, page: pageType, sessionId };
|
|
302
306
|
if (pageType === 'product' && page.productId) params.productId = page.productId;
|
|
303
307
|
if (pageType === 'category' && page.categoryId) params.categoryId = page.categoryId;
|
|
308
|
+
if (pageType === 'custom') params.customKey = customKey;
|
|
304
309
|
|
|
305
310
|
const res = await fetch(config.apiUrl + '/showcases/context?' + query(params), { headers: headers() });
|
|
306
311
|
const showcases = await res.json();
|
|
@@ -317,11 +322,13 @@ export function createRocket (config) {
|
|
|
317
322
|
};
|
|
318
323
|
|
|
319
324
|
// Uma chamada por página, repartida entre os slots (<RocketShowcase position>) — na Linx
|
|
320
|
-
// era o fetchShowCases único distribuindo por data-rocket-showcase-position.
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
+
// era o fetchShowCases único distribuindo por data-rocket-showcase-position. Por pageType +
|
|
326
|
+
// customKey: a landing e o contexto da página são chamadas distintas. O setPage zera só o contexto.
|
|
327
|
+
let pageShowcases = {};
|
|
328
|
+
rocket.getPageShowcases = function (type, customKey, refresh) {
|
|
329
|
+
const key = (type || '') + '|' + (customKey || '');
|
|
330
|
+
if (refresh || !pageShowcases[key]) pageShowcases[key] = rocket.getShowcasesByContext(type, customKey).catch(() => []);
|
|
331
|
+
return pageShowcases[key];
|
|
325
332
|
};
|
|
326
333
|
|
|
327
334
|
// Só banners (DTO banner-native). Gated pela flag `banners`: loja sem ela recebe [].
|