@mundogamernetwork/shared-ui 1.1.44 → 1.1.46
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/components/keys/KeyBrowser.vue +550 -0
- package/components/keys/KeyRevealModal.vue +142 -0
- package/package.json +1 -1
- package/pages/key/[slug].vue +125 -0
- package/pages/magazine/[slug]/preview.vue +17 -17
- package/pages/presskit/[slug].vue +4 -273
- package/pages/presskit/game/[slug].vue +291 -0
- package/plugins/echo.client.ts +1 -1
- package/services/keyService.ts +45 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Public, shareable standalone key-campaign page. Reached from broadcast links
|
|
3
|
+
// (Discord/Telegram/WhatsApp) and from anywhere — no need to enter a dashboard.
|
|
4
|
+
// Anyone can view; requesting a key uses KeyBrowser's hybrid auth (it redirects
|
|
5
|
+
// to login on 401 and returns).
|
|
6
|
+
definePageMeta({ layout: 'blank' });
|
|
7
|
+
|
|
8
|
+
const route = useRoute();
|
|
9
|
+
const slug = route.params.slug as string;
|
|
10
|
+
|
|
11
|
+
const runtimeConfig = useRuntimeConfig();
|
|
12
|
+
const cfg = (runtimeConfig.public?.mgSharedUi || {}) as Record<string, any>;
|
|
13
|
+
const apiBase: string =
|
|
14
|
+
cfg.apiBaseURL ||
|
|
15
|
+
(runtimeConfig.public as any)?.apiBaseURL ||
|
|
16
|
+
'';
|
|
17
|
+
|
|
18
|
+
const SUPPORTED_LOCALES = ['en', 'pt-BR', 'es', 'de', 'ro', 'ar-AE'];
|
|
19
|
+
const routePath = route.path.split('/');
|
|
20
|
+
const lang = SUPPORTED_LOCALES.find(l => routePath.includes(l)) || 'en';
|
|
21
|
+
|
|
22
|
+
const { data: keyData, error: fetchError, pending } = await useAsyncData(
|
|
23
|
+
`public-key-${slug}`,
|
|
24
|
+
() => $fetch<any>(`${apiBase}/public/keys/${slug}`, { params: { lang } }),
|
|
25
|
+
{ lazy: false },
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const key = computed(() => keyData.value?.data || keyData.value || null);
|
|
29
|
+
const loading = pending;
|
|
30
|
+
const error = computed(() => !!fetchError.value || (!pending.value && !key.value));
|
|
31
|
+
|
|
32
|
+
const restriction = computed(() => {
|
|
33
|
+
const k = key.value;
|
|
34
|
+
if (!k) return '';
|
|
35
|
+
if (k.is_exclusive) return 'Exclusiva para streamers oficiais';
|
|
36
|
+
if (k.is_tier_locked && k.access_tier && k.access_tier !== 'free') return `Exclusiva para o plano ${String(k.access_tier).toUpperCase()} ou superior`;
|
|
37
|
+
if (k.streamer_type_id === 2) return 'Exclusiva para streamers oficiais';
|
|
38
|
+
if (k.streamer_type_id === 3) return 'Exclusiva para afiliados';
|
|
39
|
+
return '';
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<div class="kc">
|
|
45
|
+
<div v-if="loading" class="kc__state">Carregando…</div>
|
|
46
|
+
<div v-else-if="error" class="kc__state">Campanha não encontrada.</div>
|
|
47
|
+
|
|
48
|
+
<template v-else>
|
|
49
|
+
<div class="kc__hero">
|
|
50
|
+
<img v-if="key.cover_src" :src="key.cover_src" :alt="key.name" class="kc__cover">
|
|
51
|
+
<div class="kc__hero-body">
|
|
52
|
+
<h1 class="kc__title">{{ key.name }}</h1>
|
|
53
|
+
<div v-if="restriction" class="kc__badge">🔒 {{ restriction }}</div>
|
|
54
|
+
<div class="kc__avail">
|
|
55
|
+
<span v-if="key.available_count > 0">{{ key.available_count }} chave(s) pra resgatar</span>
|
|
56
|
+
<span v-else>Chaves esgotadas</span>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="kc__browser">
|
|
62
|
+
<KeyBrowser
|
|
63
|
+
requester-context="user"
|
|
64
|
+
:game-id="key.game?.id || key.game_id"
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<style scoped lang="scss">
|
|
72
|
+
.kc {
|
|
73
|
+
max-width: 960px;
|
|
74
|
+
margin: 0 auto;
|
|
75
|
+
padding: 24px 16px 64px;
|
|
76
|
+
|
|
77
|
+
&__state {
|
|
78
|
+
padding: 80px 0;
|
|
79
|
+
text-align: center;
|
|
80
|
+
color: var(--text-secondary, #888);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
&__hero {
|
|
84
|
+
display: flex;
|
|
85
|
+
gap: 20px;
|
|
86
|
+
align-items: flex-start;
|
|
87
|
+
margin-bottom: 28px;
|
|
88
|
+
flex-wrap: wrap;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&__cover {
|
|
92
|
+
width: 260px;
|
|
93
|
+
max-width: 100%;
|
|
94
|
+
height: auto;
|
|
95
|
+
object-fit: cover;
|
|
96
|
+
border: 1px solid var(--border-color, #2a2a2a);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&__hero-body { flex: 1; min-width: 240px; display: flex; flex-direction: column; gap: 10px; }
|
|
100
|
+
|
|
101
|
+
&__title {
|
|
102
|
+
font-size: 1.6rem;
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
color: var(--text-primary, #fff);
|
|
105
|
+
margin: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
&__badge {
|
|
109
|
+
display: inline-flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 6px;
|
|
112
|
+
width: fit-content;
|
|
113
|
+
padding: 4px 10px;
|
|
114
|
+
font-size: 0.8rem;
|
|
115
|
+
font-weight: 600;
|
|
116
|
+
color: #d297ff;
|
|
117
|
+
border: 1px solid rgba(210, 151, 255, 0.35);
|
|
118
|
+
background: rgba(210, 151, 255, 0.12);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&__avail { font-size: 0.9rem; color: var(--text-secondary, #aaa); }
|
|
122
|
+
|
|
123
|
+
&__browser { margin-top: 8px; }
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
@@ -52,7 +52,7 @@ const locale = useNuxtApp().$i18n.locale;
|
|
|
52
52
|
const slug = computed(() => route.params.slug as string);
|
|
53
53
|
|
|
54
54
|
const magazineStore = useMagazineStore();
|
|
55
|
-
const { magazine, magazines, magazinePages } = magazineStore;
|
|
55
|
+
const { magazine, magazines, magazinePages } = storeToRefs(magazineStore);
|
|
56
56
|
const institutionalStore = useInstitutionalStore();
|
|
57
57
|
const { language } = institutionalStore;
|
|
58
58
|
|
|
@@ -124,7 +124,7 @@ function restoreReadingProgress() {
|
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
const totalPages = magazinePages.data?.length ?? 0;
|
|
127
|
+
const totalPages = magazinePages.value.data?.length ?? 0;
|
|
128
128
|
if (!totalPages) {
|
|
129
129
|
startPage.value = 0;
|
|
130
130
|
return;
|
|
@@ -148,15 +148,15 @@ const showDownloadGateMessage = ref("");
|
|
|
148
148
|
|
|
149
149
|
// Flipbook pages mapped for MgnFlipbook
|
|
150
150
|
const flipbookPages = computed<PageData[]>(() => {
|
|
151
|
-
if (!magazinePages.data || magazinePages.data.length === 0) return [];
|
|
152
|
-
return magazinePages.data.map((page: MagazinePage) => ({
|
|
151
|
+
if (!magazinePages.value.data || magazinePages.value.data.length === 0) return [];
|
|
152
|
+
return magazinePages.value.data.map((page: MagazinePage) => ({
|
|
153
153
|
index: page.page_number - 1,
|
|
154
154
|
thumbnailUrl: page.thumbnail_url,
|
|
155
155
|
imageUrl: page.image_url,
|
|
156
156
|
hiresUrl: page.hires_url,
|
|
157
157
|
hotspots: getMagazinePageHotspots({
|
|
158
158
|
page,
|
|
159
|
-
flipbookCode: magazine.data?.flipbook_code,
|
|
159
|
+
flipbookCode: magazine.value.data?.flipbook_code,
|
|
160
160
|
}),
|
|
161
161
|
}));
|
|
162
162
|
});
|
|
@@ -164,14 +164,14 @@ const flipbookPages = computed<PageData[]>(() => {
|
|
|
164
164
|
// Determine if lead capture modal should show
|
|
165
165
|
const requiresLeadCapture = computed(() => {
|
|
166
166
|
if (leadCaptured.value) return false;
|
|
167
|
-
if (!magazinePages.meta) return false;
|
|
168
|
-
return magazinePages.meta.requires_lead || magazinePages.meta.capture_leads;
|
|
167
|
+
if (!magazinePages.value.meta) return false;
|
|
168
|
+
return magazinePages.value.meta.requires_lead || magazinePages.value.meta.capture_leads;
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
async function getMagazineData() {
|
|
172
172
|
try {
|
|
173
173
|
await magazineStore.getMagazine(slug.value);
|
|
174
|
-
status.value = magazine.status;
|
|
174
|
+
status.value = magazine.value.status;
|
|
175
175
|
} catch (error) {
|
|
176
176
|
console.error("Error fetching magazine:", error);
|
|
177
177
|
}
|
|
@@ -195,8 +195,8 @@ async function loadFlipbookPages() {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
function downloadPdf() {
|
|
198
|
-
if (magazine.data.pdf_url_src) {
|
|
199
|
-
window.open(magazine.data.pdf_url_src, "_blank");
|
|
198
|
+
if (magazine.value.data.pdf_url_src) {
|
|
199
|
+
window.open(magazine.value.data.pdf_url_src, "_blank");
|
|
200
200
|
magazineStore.trackAnalytic(slug.value, "download");
|
|
201
201
|
}
|
|
202
202
|
}
|
|
@@ -210,13 +210,13 @@ function downloadPdf() {
|
|
|
210
210
|
*/
|
|
211
211
|
function handleFlipbookDownload() {
|
|
212
212
|
// Gate 1: Premium magazine - need active subscription
|
|
213
|
-
if (magazine.data.premium === true) {
|
|
213
|
+
if (magazine.value.data.premium === true) {
|
|
214
214
|
showDownloadGateMessage.value = "premium";
|
|
215
215
|
return;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// Gate 2: Non-public magazine - must be registered/signed in
|
|
219
|
-
if (magazine.data.public === false && signedIn.value === false) {
|
|
219
|
+
if (magazine.value.data.public === false && signedIn.value === false) {
|
|
220
220
|
showDownloadGateMessage.value = "signin";
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
@@ -307,7 +307,7 @@ async function fetchMagazine() {
|
|
|
307
307
|
await magazineStore.fetchMagazine({
|
|
308
308
|
filter: {
|
|
309
309
|
status: 1,
|
|
310
|
-
edition_number: magazine.data.edition_number,
|
|
310
|
+
edition_number: magazine.value.data.edition_number,
|
|
311
311
|
platform_id: import.meta.env.VITE_SYSTEM_ID,
|
|
312
312
|
},
|
|
313
313
|
sort: "id",
|
|
@@ -331,16 +331,16 @@ async function fetchLanguage() {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
function filterLanguages() {
|
|
334
|
-
if (magazine.data && magazines.data && magazines.data.length > 0) {
|
|
335
|
-
languageId.value = magazine.data.language.id;
|
|
336
|
-
const languageIds = magazines.data.map(
|
|
334
|
+
if (magazine.value.data && magazines.value.data && magazines.value.data.length > 0) {
|
|
335
|
+
languageId.value = magazine.value.data.language.id;
|
|
336
|
+
const languageIds = magazines.value.data.map(
|
|
337
337
|
(item: MagazineItem) => item.language.id,
|
|
338
338
|
);
|
|
339
339
|
languages.value = language.data.filter((language: LanguageItem) =>
|
|
340
340
|
languageIds.includes(language.id),
|
|
341
341
|
);
|
|
342
342
|
languages.value.forEach((language: LanguageItem) => {
|
|
343
|
-
const magazineItem = magazines.data.find(
|
|
343
|
+
const magazineItem = magazines.value.data.find(
|
|
344
344
|
(item: MagazineItem) => item.language.id === language.id,
|
|
345
345
|
);
|
|
346
346
|
if (magazineItem) {
|
|
@@ -1,275 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const route = useRoute();
|
|
7
|
-
const slug = route.params.slug as string;
|
|
8
|
-
|
|
9
|
-
const runtimeConfig = useRuntimeConfig();
|
|
10
|
-
const cfg = (runtimeConfig.public?.mgSharedUi || {}) as Record<string, any>;
|
|
11
|
-
// Consuming apps may set apiBaseURL at public root (agency/community/tv pattern)
|
|
12
|
-
// or inside mgSharedUi. pressKitApiUrl overrides both (e.g. community points to agency API).
|
|
13
|
-
const apiBase: string =
|
|
14
|
-
cfg.pressKitApiUrl ||
|
|
15
|
-
(runtimeConfig.public as any)?.pressKitApiUrl ||
|
|
16
|
-
cfg.apiBaseURL ||
|
|
17
|
-
(runtimeConfig.public as any)?.apiBaseURL ||
|
|
18
|
-
'';
|
|
19
|
-
|
|
20
|
-
const agencyBase: string =
|
|
21
|
-
cfg.agencyBaseUrl ||
|
|
22
|
-
(runtimeConfig.public as any)?.agencyBaseUrl ||
|
|
23
|
-
'';
|
|
24
|
-
|
|
25
|
-
const SUPPORTED_LOCALES = ['en', 'pt-BR', 'es', 'de', 'ro', 'ar-AE'];
|
|
26
|
-
const routePath = route.path.split('/');
|
|
27
|
-
const lang = SUPPORTED_LOCALES.find(l => routePath.includes(l)) || 'en';
|
|
28
|
-
|
|
29
|
-
const { data: kitData, error: fetchError, pending } = await useAsyncData(
|
|
30
|
-
`press-kit-${slug}`,
|
|
31
|
-
() => $fetch<any>(`${apiBase}/public/press-kits/${slug}`, {
|
|
32
|
-
params: { include: 'assets,videos,credits,awards,quotes', lang },
|
|
33
|
-
}),
|
|
34
|
-
{ lazy: false },
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const kit = computed(() => kitData.value?.data || kitData.value || null);
|
|
38
|
-
const loading = pending;
|
|
39
|
-
const error = computed(() => !!fetchError.value || (!pending.value && !kit.value));
|
|
40
|
-
|
|
41
|
-
onMounted(() => {
|
|
42
|
-
if (kit.value) {
|
|
43
|
-
trackPressKitEvent(slug, {
|
|
44
|
-
event_type: 'view',
|
|
45
|
-
referrer: document.referrer || undefined,
|
|
46
|
-
}).catch(() => {});
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Fractal include shape: relation -> { data: [...] }
|
|
51
|
-
const list = (rel: any) => rel?.data || rel || [];
|
|
52
|
-
const assets = computed(() => list(kit.value?.assets));
|
|
53
|
-
const videos = computed(() => list(kit.value?.videos));
|
|
54
|
-
const awards = computed(() => list(kit.value?.awards));
|
|
55
|
-
const quotes = computed(() => list(kit.value?.quotes));
|
|
56
|
-
const team = computed(() => list(kit.value?.credits));
|
|
57
|
-
const features = computed(() => kit.value?.key_features || []);
|
|
58
|
-
// New, optional blocks (rendered only when the API provides them)
|
|
59
|
-
const articles = computed(() => list(kit.value?.articles));
|
|
60
|
-
const coverage = computed(() => list(kit.value?.coverage));
|
|
61
|
-
const showcases = computed(() => list(kit.value?.showcases));
|
|
62
|
-
const hasEcosystem = computed(() => showcases.value.length || kit.value?.ventures_url || kit.value?.magazine_feature || kit.value?.community_url);
|
|
63
|
-
|
|
64
|
-
const screenshots = computed(() => assets.value.filter((a: any) => ['screenshot', 'artwork', 'gif'].includes(a.type)));
|
|
65
|
-
const brandAssets = computed(() => assets.value.filter((a: any) => ['logo', 'document'].includes(a.type)));
|
|
66
|
-
|
|
67
|
-
const tpl = computed(() => kit.value?.theme || null);
|
|
68
|
-
const layout = computed(() => kit.value?.layout || 'classic');
|
|
69
|
-
const allowDownload = computed(() => kit.value?.allow_asset_download !== false);
|
|
70
|
-
|
|
71
|
-
const socials = computed(() => {
|
|
72
|
-
const s = kit.value?.social_links || {};
|
|
73
|
-
return Object.entries(s).filter(([, v]) => v).map(([k, v]) => ({ platform: k, url: v as string }));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const facts = computed(() => {
|
|
77
|
-
const k = kit.value || {};
|
|
78
|
-
const out: { label: string; value: string; accent?: boolean; href?: string }[] = [];
|
|
79
|
-
if (k.developer) out.push({ label: 'kit.press.developer', value: k.developer });
|
|
80
|
-
if (k.publisher) out.push({ label: 'kit.press.publisher', value: k.publisher });
|
|
81
|
-
if (k.release_date_tba) out.push({ label: 'kit.press.release', value: 'TBA', accent: true });
|
|
82
|
-
else if (k.release_date) out.push({ label: 'kit.press.release', value: k.release_date, accent: true });
|
|
83
|
-
if (k.release_status) out.push({ label: 'kit.press.status', value: String(k.release_status).replace(/_/g, ' ') });
|
|
84
|
-
if (k.price) out.push({ label: 'kit.press.price', value: `${k.price}${k.currency ? ' ' + k.currency : ''}` });
|
|
85
|
-
if (k.website_url) out.push({ label: 'kit.press.website', value: k.website_url, accent: true, href: k.website_url });
|
|
86
|
-
if (k.steam_url) out.push({ label: 'Steam', value: k.steam_url, href: k.steam_url });
|
|
87
|
-
if (k.press_contact_email) out.push({ label: 'kit.press.contact', value: k.press_contact_email, href: `mailto:${k.press_contact_email}` });
|
|
88
|
-
return out;
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
function track(type: 'click' | 'download' | 'contact') {
|
|
92
|
-
trackPressKitEvent(slug, { event_type: type, referrer: document.referrer || undefined }).catch(() => {});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const requestUrl = useRequestURL();
|
|
96
|
-
useHead(() => ({
|
|
97
|
-
title: kit.value ? `${kit.value.name} — Press Kit` : 'Press Kit',
|
|
98
|
-
meta: [
|
|
99
|
-
{ name: 'description', content: kit.value?.tagline || '' },
|
|
100
|
-
{ property: 'og:title', content: kit.value?.name || 'Press Kit' },
|
|
101
|
-
{ property: 'og:description', content: kit.value?.tagline || '' },
|
|
102
|
-
{ property: 'og:image', content: kit.value?.hero_image_url || kit.value?.logo_url || '' },
|
|
103
|
-
{ property: 'og:type', content: 'article' },
|
|
104
|
-
{ property: 'og:url', content: requestUrl.href },
|
|
105
|
-
],
|
|
106
|
-
}));
|
|
2
|
+
// Redirect /presskit/[slug] → /presskit/game/[slug] (canonical press kit route)
|
|
3
|
+
definePageMeta({ layout: 'blank' })
|
|
4
|
+
const route = useRoute()
|
|
5
|
+
navigateTo(`/presskit/game/${route.params.slug}`, { redirectCode: 301 })
|
|
107
6
|
</script>
|
|
108
|
-
|
|
109
|
-
<template>
|
|
110
|
-
<div class="kit-press" :data-tpl="tpl" :data-layout="layout">
|
|
111
|
-
<div v-if="loading" class="kit-state">{{ $t('kit.loading') }}</div>
|
|
112
|
-
<div v-else-if="error || !kit" class="kit-state">
|
|
113
|
-
<h2>{{ $t('kit.press.not_found') }}</h2>
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
<template v-else>
|
|
117
|
-
<!-- CAPA / COVER -->
|
|
118
|
-
<header class="kit-cover">
|
|
119
|
-
<img v-if="kit.hero_image_url" class="kit-cover-art" :src="kit.hero_image_url" :alt="kit.name" />
|
|
120
|
-
<div class="kit-cover-inner">
|
|
121
|
-
<img v-if="kit.cover_image_url" class="kit-cover-poster" :src="kit.cover_image_url" :alt="kit.name" />
|
|
122
|
-
<div class="kit-cover-text">
|
|
123
|
-
<span v-if="kit.release_status" class="kit-eyebrow">{{ String(kit.release_status).replace(/_/g, ' ') }}</span>
|
|
124
|
-
<img v-if="kit.logo_url" class="kit-cover-logo-img" :src="kit.logo_url" :alt="kit.name" />
|
|
125
|
-
<h1 v-else>{{ kit.name }}</h1>
|
|
126
|
-
<p v-if="kit.tagline" class="kit-tagline">{{ kit.tagline }}</p>
|
|
127
|
-
<p v-if="kit.developer" class="kit-kicker">{{ kit.developer }}</p>
|
|
128
|
-
<div class="kit-actions">
|
|
129
|
-
<a v-if="allowDownload" class="kit-btn kit-btn-primary" @click="track('download')">{{ $t('kit.press.download_assets') }}</a>
|
|
130
|
-
<a v-if="kit.press_contact_email" class="kit-btn kit-btn-ghost" :href="`mailto:${kit.press_contact_email}`" @click="track('contact')">{{ $t('kit.press.request_key') }}</a>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
134
|
-
</header>
|
|
135
|
-
|
|
136
|
-
<div class="kit-wrap">
|
|
137
|
-
<div class="kit-layout">
|
|
138
|
-
<aside class="kit-sidebar">
|
|
139
|
-
<div class="kit-card kit-factsheet">
|
|
140
|
-
<h3 class="kit-fact-title">{{ $t('kit.press.fact_sheet') }}</h3>
|
|
141
|
-
<div v-for="f in facts" :key="f.label" class="kit-fact">
|
|
142
|
-
<span class="k">{{ $te(f.label) ? $t(f.label) : f.label }}</span>
|
|
143
|
-
<a v-if="f.href" class="v accent" :href="f.href" target="_blank" rel="noopener" @click="track('click')">{{ f.value }}</a>
|
|
144
|
-
<span v-else class="v" :class="{ accent: f.accent }">{{ f.value }}</span>
|
|
145
|
-
</div>
|
|
146
|
-
<div v-if="socials.length" class="kit-fact">
|
|
147
|
-
<span class="k">{{ $t('kit.press.social') }}</span>
|
|
148
|
-
<div class="kit-socials">
|
|
149
|
-
<a v-for="s in socials" :key="s.platform" :href="s.url" target="_blank" rel="noopener" @click="track('click')">{{ s.platform.charAt(0).toUpperCase() }}</a>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
</aside>
|
|
154
|
-
|
|
155
|
-
<main class="kit-main">
|
|
156
|
-
<section v-if="kit.description" class="kit-block">
|
|
157
|
-
<h2>{{ $t('kit.press.about') }}</h2>
|
|
158
|
-
<div class="kit-lead" v-html="kit.description" />
|
|
159
|
-
</section>
|
|
160
|
-
|
|
161
|
-
<section v-if="features.length" class="kit-block">
|
|
162
|
-
<h2>{{ $t('kit.press.features') }}</h2>
|
|
163
|
-
<div class="kit-features">
|
|
164
|
-
<div v-for="(f, i) in features" :key="i" class="kit-feature"><span class="m">▸</span>{{ f }}</div>
|
|
165
|
-
</div>
|
|
166
|
-
</section>
|
|
167
|
-
|
|
168
|
-
<section v-if="videos.length" class="kit-block">
|
|
169
|
-
<h2>{{ $t('kit.press.videos') }}</h2>
|
|
170
|
-
<PressKitVideoPlayer :videos="videos" />
|
|
171
|
-
</section>
|
|
172
|
-
|
|
173
|
-
<section v-if="screenshots.length" class="kit-block">
|
|
174
|
-
<div class="kit-shead"><h2>{{ $t('kit.press.screenshots') }}</h2><a v-if="allowDownload" class="kit-dl" @click="track('download')">{{ $t('kit.press.download_all') }}</a></div>
|
|
175
|
-
<div class="kit-gallery">
|
|
176
|
-
<a v-for="(s, i) in screenshots" :key="i" class="kit-shot" :href="s.url" target="_blank" rel="noopener"><img :src="s.thumbnail_url || s.url" :alt="s.title || ''" /></a>
|
|
177
|
-
</div>
|
|
178
|
-
</section>
|
|
179
|
-
|
|
180
|
-
<section v-if="brandAssets.length" class="kit-block">
|
|
181
|
-
<div class="kit-shead"><h2>{{ $t('kit.press.logo_icons') }}</h2></div>
|
|
182
|
-
<div class="kit-logos">
|
|
183
|
-
<a v-for="(b, i) in brandAssets" :key="i" class="kit-logo-tile" :href="b.url" target="_blank" rel="noopener" @click="track('download')">
|
|
184
|
-
<span class="ic">◆</span>{{ b.title || b.type }}
|
|
185
|
-
</a>
|
|
186
|
-
</div>
|
|
187
|
-
</section>
|
|
188
|
-
|
|
189
|
-
<!-- NEW: Conteúdos & Artigos (community + external) -->
|
|
190
|
-
<section v-if="articles.length" class="kit-block">
|
|
191
|
-
<div class="kit-shead"><h2>{{ $t('kit.press.articles') }}</h2></div>
|
|
192
|
-
<div class="kit-articles">
|
|
193
|
-
<a v-for="(a, i) in articles" :key="i" class="kit-article" :href="a.url" target="_blank" rel="noopener" @click="track('click')">
|
|
194
|
-
<div class="thumb" :style="a.image ? { backgroundImage: `url(${a.image})` } : {}"><span class="src" :class="{ ext: a.external }">{{ a.external ? $t('kit.press.external') : 'Mundo Gamer' }}</span></div>
|
|
195
|
-
<div class="a-body"><h4>{{ a.title }}</h4><div class="meta">{{ a.source }}<span v-if="a.date"> · {{ a.date }}</span></div></div>
|
|
196
|
-
</a>
|
|
197
|
-
</div>
|
|
198
|
-
</section>
|
|
199
|
-
|
|
200
|
-
<!-- NEW: Cobertura de Criadores -->
|
|
201
|
-
<section v-if="coverage.length" class="kit-block">
|
|
202
|
-
<h2>{{ $t('kit.press.coverage') }}</h2>
|
|
203
|
-
<div class="kit-creators">
|
|
204
|
-
<a v-for="(c, i) in coverage" :key="i" class="kit-creator" :href="c.url || undefined" target="_blank" rel="noopener">
|
|
205
|
-
<div class="av" :style="c.avatar ? { backgroundImage: `url(${c.avatar})` } : {}" />
|
|
206
|
-
<div class="cn">{{ c.name }}</div>
|
|
207
|
-
<div class="cp">{{ c.platform }}<span v-if="c.followers"> · {{ c.followers }}</span></div>
|
|
208
|
-
<div v-if="c.label" class="cv">{{ c.label }}</div>
|
|
209
|
-
</a>
|
|
210
|
-
</div>
|
|
211
|
-
</section>
|
|
212
|
-
|
|
213
|
-
<section v-if="awards.length" class="kit-block">
|
|
214
|
-
<h2>{{ $t('kit.press.awards') }}</h2>
|
|
215
|
-
<PressKitAwards :awards="awards" />
|
|
216
|
-
</section>
|
|
217
|
-
|
|
218
|
-
<section v-if="quotes.length" class="kit-block">
|
|
219
|
-
<h2>{{ $t('kit.press.press') }}</h2>
|
|
220
|
-
<PressKitQuotes :quotes="quotes" />
|
|
221
|
-
</section>
|
|
222
|
-
|
|
223
|
-
<!-- Créditos -> Equipe -->
|
|
224
|
-
<section v-if="team.length" class="kit-block">
|
|
225
|
-
<h2>{{ $t('kit.press.team') }}</h2>
|
|
226
|
-
<div class="kit-team">
|
|
227
|
-
<div v-for="(m, i) in team" :key="i" class="kit-member"><span>{{ m.name }}</span><span class="role">{{ m.role }}</span></div>
|
|
228
|
-
</div>
|
|
229
|
-
</section>
|
|
230
|
-
|
|
231
|
-
<!-- NEW: Ecossistema Mundo Gamer -->
|
|
232
|
-
<section v-if="hasEcosystem" class="kit-block">
|
|
233
|
-
<h2>{{ $t('kit.press.ecosystem') }}</h2>
|
|
234
|
-
<div class="kit-eco">
|
|
235
|
-
<div v-if="showcases.length" class="kit-eco-card full">
|
|
236
|
-
<div class="kit-eco-h"><span class="ic">★</span>{{ $t('kit.press.showcases') }}</div>
|
|
237
|
-
<div class="kit-sbadges">
|
|
238
|
-
<span v-for="(s, i) in showcases" :key="i" class="kit-sbadge" :class="{ feat: s.featured }"><span class="medal">{{ s.featured ? '🏵️' : '🎮' }}</span>{{ s.name }}<span v-if="s.year"> {{ s.year }}</span></span>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
<div v-if="kit.ventures_url" class="kit-eco-card">
|
|
242
|
-
<div class="kit-eco-h"><span class="ic">◈</span>MGN Ventures</div>
|
|
243
|
-
<a class="kit-vrow" :href="kit.ventures_url" target="_blank" rel="noopener" @click="track('click')"><span class="vlogo">VENTURES</span><span class="vtxt">{{ $t('kit.press.ventures_text') }}</span></a>
|
|
244
|
-
</div>
|
|
245
|
-
<a v-if="kit.magazine_feature" class="kit-eco-card kit-mag" :href="kit.magazine_feature.url" target="_blank" rel="noopener" @click="track('click')">
|
|
246
|
-
<div class="kit-eco-h" style="width:100%"><span class="ic">▤</span>MGN Magazine</div>
|
|
247
|
-
<div class="kit-mag-row"><div class="mcover" :style="kit.magazine_feature.cover ? { backgroundImage: `url(${kit.magazine_feature.cover})` } : {}" /><div><div class="mtitle">{{ kit.magazine_feature.title }}</div><div class="mmeta">{{ kit.magazine_feature.issue }}</div></div></div>
|
|
248
|
-
</a>
|
|
249
|
-
<div v-if="kit.community_url" class="kit-eco-card full">
|
|
250
|
-
<div class="kit-ccta">
|
|
251
|
-
<div><div class="ctitle">{{ $t('kit.press.follow_community', { name: kit.name }) }}</div><div v-if="kit.community_followers" class="cmeta">{{ kit.community_followers }} {{ $t('kit.press.followers') }}</div></div>
|
|
252
|
-
<a class="kit-btn kit-btn-primary" :href="kit.community_url" target="_blank" rel="noopener" @click="track('click')">{{ $t('kit.press.follow') }}</a>
|
|
253
|
-
</div>
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
256
|
-
</section>
|
|
257
|
-
|
|
258
|
-
<section v-if="kit.press_contact_email" class="kit-block">
|
|
259
|
-
<div class="kit-cta">
|
|
260
|
-
<h2>{{ $t('kit.press.covering', { name: kit.name }) }}</h2>
|
|
261
|
-
<p>{{ $t('kit.press.cta_text') }}</p>
|
|
262
|
-
<a class="kit-btn kit-btn-primary" :href="`mailto:${kit.press_contact_email}`" @click="track('contact')">{{ $t('kit.press.request_key') }}</a>
|
|
263
|
-
</div>
|
|
264
|
-
</section>
|
|
265
|
-
|
|
266
|
-
<p class="kit-madewith">
|
|
267
|
-
{{ $t('kit.press.made_with') }} <strong>Mundo Gamer</strong> —
|
|
268
|
-
<a :href="kit.create_kit_url || `${agencyBase}/presskit`" target="_blank" rel="noopener">{{ $t('kit.press.create_yours') }}</a>
|
|
269
|
-
</p>
|
|
270
|
-
</main>
|
|
271
|
-
</div>
|
|
272
|
-
</div>
|
|
273
|
-
</template>
|
|
274
|
-
</div>
|
|
275
|
-
</template>
|