@mundogamernetwork/shared-ui 1.1.36 → 1.1.38

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.
@@ -1,28 +1,49 @@
1
1
  <script setup lang="ts">
2
- import { fetchPublicPressKit, trackPressKitEvent } from '../../services/pressKitService';
2
+ import { trackPressKitEvent } from '../../services/pressKitService';
3
3
 
4
- definePageMeta({ ssr: false, layout: 'blank' });
4
+ definePageMeta({ layout: 'blank' });
5
5
 
6
6
  const route = useRoute();
7
7
  const slug = route.params.slug as string;
8
8
 
9
- const kit = ref<Record<string, any> | null>(null);
10
- const loading = ref(true);
11
- const error = ref(false);
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
+ '';
12
19
 
13
- onMounted(async () => {
14
- try {
15
- const { data } = await fetchPublicPressKit(slug);
16
- kit.value = data?.data || data;
20
+ const agencyBase: string =
21
+ cfg.agencyBaseUrl ||
22
+ (runtimeConfig.public as any)?.agencyBaseUrl ||
23
+ '';
17
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) {
18
43
  trackPressKitEvent(slug, {
19
44
  event_type: 'view',
20
45
  referrer: document.referrer || undefined,
21
46
  }).catch(() => {});
22
- } catch {
23
- error.value = true;
24
- } finally {
25
- loading.value = false;
26
47
  }
27
48
  });
28
49
 
@@ -244,7 +265,7 @@ useHead(() => ({
244
265
 
245
266
  <p class="kit-madewith">
246
267
  {{ $t('kit.press.made_with') }} <strong>Mundo Gamer</strong> —
247
- <a :href="kit.create_kit_url || '/dashboard/press-kit'">{{ $t('kit.press.create_yours') }}</a>
268
+ <a :href="kit.create_kit_url || `${agencyBase}/presskit`" target="_blank" rel="noopener">{{ $t('kit.press.create_yours') }}</a>
248
269
  </p>
249
270
  </main>
250
271
  </div>
@@ -0,0 +1,83 @@
1
+ import {
2
+ fetchMagazine,
3
+ getMagazine,
4
+ getMagazinePages,
5
+ trackMagazineAnalytic,
6
+ captureMagazineLead,
7
+ } from "../services/institutionalService";
8
+ import { defineStore } from "pinia";
9
+ import type { MagazinePage, MagazinePagesMeta } from "../types/magazine";
10
+
11
+ export const useMagazineStore = defineStore({
12
+ id: "magazine-store",
13
+ state: () => {
14
+ return {
15
+ magazines: {
16
+ data: <any>{},
17
+ meta: <any>{},
18
+ status: "init",
19
+ },
20
+ magazine: {
21
+ data: <any>{},
22
+ status: "init",
23
+ },
24
+ magazinePages: {
25
+ data: <MagazinePage[]>[],
26
+ meta: <MagazinePagesMeta | null>null,
27
+ status: "init",
28
+ },
29
+ };
30
+ },
31
+ actions: {
32
+ async fetchMagazine(params: any) {
33
+ this.magazine.status = "pending";
34
+ const { data } = await fetchMagazine(params);
35
+
36
+ this.magazines.data = data.data;
37
+ this.magazines.meta = data.meta;
38
+
39
+ this.magazines.status = "success";
40
+ },
41
+ async getMagazine(magazine: string) {
42
+ this.magazine.status = "pending";
43
+ const { data } = await getMagazine(magazine);
44
+
45
+ this.magazine.data = data.data;
46
+
47
+ this.magazine.status = "success";
48
+ },
49
+ async fetchMagazinePages(slug: string) {
50
+ this.magazinePages.status = "pending";
51
+ try {
52
+ const { data } = await getMagazinePages(slug);
53
+ this.magazinePages.data = data.data;
54
+ this.magazinePages.meta = data.meta;
55
+ this.magazinePages.status = "success";
56
+ } catch (error) {
57
+ this.magazinePages.status = "error";
58
+ this.magazinePages.data = [];
59
+ this.magazinePages.meta = null;
60
+ }
61
+ },
62
+ async trackAnalytic(slug: string, eventType: string) {
63
+ try {
64
+ await trackMagazineAnalytic(slug, eventType);
65
+ } catch (error) {
66
+ console.error("Error tracking analytic:", error);
67
+ }
68
+ },
69
+ async captureLead(slug: string, name: string, email: string) {
70
+ try {
71
+ await captureMagazineLead(slug, name, email);
72
+ return true;
73
+ } catch (error) {
74
+ console.error("Error capturing lead:", error);
75
+ return false;
76
+ }
77
+ },
78
+ },
79
+ });
80
+
81
+ if (import.meta.hot) {
82
+ import.meta.hot.accept(acceptHMRUpdate(useMagazineStore, import.meta.hot));
83
+ }
@@ -0,0 +1,103 @@
1
+ export interface MagazinePageHotspot {
2
+ id: string;
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ url?: string;
8
+ target?: "_self" | "_blank";
9
+ title?: string;
10
+ analytics_event?: string;
11
+ data?: unknown;
12
+ }
13
+
14
+ export interface MagazinePage {
15
+ page_number: number;
16
+ thumbnail_url: string;
17
+ image_url: string;
18
+ hires_url: string | null;
19
+ hotspots?: MagazinePageHotspot[];
20
+ }
21
+
22
+ export interface MagazinePagesMeta {
23
+ total_pages: number;
24
+ magazine_id: number;
25
+ flipbook_config_version?: number | null;
26
+ requires_lead: boolean;
27
+ capture_leads: boolean;
28
+ is_premium: boolean;
29
+ total_views: number;
30
+ total_downloads: number;
31
+ total_leads: number;
32
+ }
33
+
34
+ export interface Magazine {
35
+ id: number;
36
+ name: string;
37
+ short_description: string;
38
+ description: string;
39
+ edition_number: string;
40
+ release_date: string;
41
+ cover_url: string;
42
+ pdf_url: string;
43
+ status: boolean;
44
+ public: boolean;
45
+ premium: boolean;
46
+ worldwide: boolean;
47
+ capture_leads: boolean;
48
+ language_id: number;
49
+ views: number;
50
+ created_at: string;
51
+ updated_at: string;
52
+ color: string;
53
+ slug: string;
54
+ flipbook_code: string | null;
55
+ preview: string | null;
56
+ pdf_url_src: string;
57
+ cover_url_original_src: string;
58
+ cover_url_src: string;
59
+ language: {
60
+ id: number;
61
+ name: string;
62
+ name_abrev: string;
63
+ status: boolean;
64
+ icon: string | null;
65
+ created_at: string;
66
+ updated_at: string;
67
+ flag_svg: string;
68
+ status_public: boolean;
69
+ native_name: string;
70
+ flag_url: string;
71
+ localized_name: string;
72
+ };
73
+ platforms: Array<{
74
+ id: number;
75
+ name: string;
76
+ short_name: string;
77
+ url_image: string;
78
+ color: string;
79
+ prod_url: string;
80
+ dev_url: string;
81
+ local_url: string;
82
+ created_at: string;
83
+ updated_at: string;
84
+ url_reduced_image: string;
85
+ prod_url_api: string;
86
+ dev_url_api: string;
87
+ local_url_api: string;
88
+ health_check_endpoint: string;
89
+ health_check_endpoint_api: string;
90
+ status: boolean;
91
+ users_count: number;
92
+ domains_count: number;
93
+ contacts_count: number;
94
+ url_image_original_src: string;
95
+ url_image_src: string;
96
+ url_reduced_image_original_src: string;
97
+ url_reduced_image_src: string;
98
+ pivot: {
99
+ magazine_id: number;
100
+ mg_platform_id: number;
101
+ };
102
+ }>;
103
+ }
@@ -0,0 +1,62 @@
1
+ import type { PageHotspot } from "@mundogamernetwork/mgn-flipbook";
2
+ import type { MagazinePage, MagazinePageHotspot } from "../types/magazine";
3
+
4
+ type HotspotsByPage = Record<number, MagazinePageHotspot[]>;
5
+
6
+ interface FlipbookHotspotConfig {
7
+ version?: number;
8
+ hotspotsByPage?: HotspotsByPage;
9
+ }
10
+
11
+ export function parseFlipbookHotspotConfig(
12
+ flipbookCode?: string | null,
13
+ ): FlipbookHotspotConfig {
14
+ if (!flipbookCode) return {};
15
+
16
+ try {
17
+ const parsed = JSON.parse(flipbookCode) as FlipbookHotspotConfig;
18
+ return parsed && typeof parsed === "object" ? parsed : {};
19
+ } catch (error) {
20
+ return {};
21
+ }
22
+ }
23
+
24
+ function normalizeHotspot(
25
+ hotspot: MagazinePageHotspot,
26
+ ): PageHotspot {
27
+ return {
28
+ id: hotspot.id,
29
+ x: hotspot.x,
30
+ y: hotspot.y,
31
+ width: hotspot.width,
32
+ height: hotspot.height,
33
+ url: hotspot.url,
34
+ target: hotspot.target,
35
+ title: hotspot.title,
36
+ data: {
37
+ analyticsEvent: hotspot.analytics_event,
38
+ ...(typeof hotspot.data === "object" && hotspot.data !== null
39
+ ? hotspot.data
40
+ : hotspot.data !== undefined
41
+ ? { value: hotspot.data }
42
+ : {}),
43
+ },
44
+ };
45
+ }
46
+
47
+ export function getMagazinePageHotspots({
48
+ page,
49
+ flipbookCode,
50
+ }: {
51
+ page: MagazinePage;
52
+ flipbookCode?: string | null;
53
+ }): PageHotspot[] {
54
+ const apiHotspots = page.hotspots ?? [];
55
+ if (apiHotspots.length > 0) {
56
+ return apiHotspots.map(normalizeHotspot);
57
+ }
58
+
59
+ const config = parseFlipbookHotspotConfig(flipbookCode);
60
+ const configuredHotspots = config.hotspotsByPage?.[page.page_number] ?? [];
61
+ return configuredHotspots.map(normalizeHotspot);
62
+ }
@@ -1,209 +0,0 @@
1
- <script setup lang="ts">
2
- import { getMagazine, getMagazinePages, trackMagazineAnalytic } from "../../services/institutionalService";
3
-
4
- definePageMeta({ layout: "default" });
5
-
6
- const route = useRoute();
7
- const slug = route.params.slug as string;
8
-
9
- const magazine = ref<any>(null);
10
- const pages = ref<any[]>([]);
11
- const loading = ref(true);
12
- const currentPageIndex = ref(0);
13
-
14
- async function fetchMagazineData() {
15
- loading.value = true;
16
- try {
17
- const [magResponse, pagesResponse] = await Promise.all([
18
- getMagazine(slug),
19
- getMagazinePages(slug),
20
- ]);
21
- magazine.value = magResponse.data?.data || magResponse.data;
22
- pages.value = pagesResponse.data?.data || pagesResponse.data || [];
23
-
24
- trackMagazineAnalytic(slug, "view").catch(() => {});
25
- } catch {
26
- throw createError({ statusCode: 404, statusMessage: "Magazine not found" });
27
- } finally {
28
- loading.value = false;
29
- }
30
- }
31
-
32
- function nextPage() {
33
- if (currentPageIndex.value < pages.value.length - 1) {
34
- currentPageIndex.value++;
35
- }
36
- }
37
-
38
- function prevPage() {
39
- if (currentPageIndex.value > 0) {
40
- currentPageIndex.value--;
41
- }
42
- }
43
-
44
- const currentPage = computed(() => pages.value[currentPageIndex.value] || null);
45
-
46
- onMounted(() => {
47
- fetchMagazineData();
48
- });
49
- </script>
50
-
51
- <template>
52
- <div id="magazine-detail" class="container magazine-detail">
53
- <div v-if="loading" class="loading">
54
- <p>{{ $t("components.loading") }}</p>
55
- </div>
56
-
57
- <template v-else-if="magazine">
58
- <div class="magazine-header">
59
- <h1>{{ magazine.title }}</h1>
60
- <p v-if="magazine.description">{{ magazine.description }}</p>
61
- <div class="meta">
62
- <span v-if="magazine.edition_number">{{ $t("more.magazine.edition") }} #{{ magazine.edition_number }}</span>
63
- <span>{{ magazine.release_date || magazine.created_at }}</span>
64
- </div>
65
- </div>
66
-
67
- <div v-if="pages.length > 0" class="magazine-reader">
68
- <div class="page-content">
69
- <img
70
- v-if="currentPage?.image_url"
71
- :src="currentPage.image_url"
72
- :alt="`Page ${currentPageIndex + 1}`"
73
- class="page-image"
74
- />
75
- <div v-else-if="currentPage?.content" class="page-text" v-html="currentPage.content"></div>
76
- </div>
77
-
78
- <div class="page-controls">
79
- <button :disabled="currentPageIndex <= 0" @click="prevPage">
80
- <MGIcon icon="chevron-left" />
81
- {{ $t("components.previous") }}
82
- </button>
83
- <span>{{ currentPageIndex + 1 }} / {{ pages.length }}</span>
84
- <button :disabled="currentPageIndex >= pages.length - 1" @click="nextPage">
85
- {{ $t("components.next") }}
86
- <MGIcon icon="chevron-right" />
87
- </button>
88
- </div>
89
- </div>
90
-
91
- <div v-else-if="magazine.pdf_url" class="magazine-pdf">
92
- <a :href="magazine.pdf_url" target="_blank" rel="noopener" class="download-btn">
93
- {{ $t("more.magazine.download_pdf") }}
94
- </a>
95
- </div>
96
- </template>
97
- </div>
98
- </template>
99
-
100
- <style lang="scss" scoped>
101
- .magazine-detail {
102
- color: var(--inactive);
103
- padding: 24px 0;
104
-
105
- .magazine-header {
106
- text-align: center;
107
- margin-bottom: 32px;
108
-
109
- h1 {
110
- font-size: 24px;
111
- font-weight: 600;
112
- color: var(--card-cover-title);
113
- margin-bottom: 8px;
114
- }
115
-
116
- p {
117
- font-size: 14px;
118
- line-height: 20px;
119
- max-width: 600px;
120
- margin: 0 auto 12px;
121
- }
122
-
123
- .meta {
124
- display: flex;
125
- justify-content: center;
126
- gap: 16px;
127
- font-size: 12px;
128
- color: var(--secondary-info-fg);
129
- }
130
- }
131
-
132
- .magazine-reader {
133
- max-width: 800px;
134
- margin: 0 auto;
135
- }
136
-
137
- .page-content {
138
- min-height: 500px;
139
- display: flex;
140
- justify-content: center;
141
- align-items: center;
142
- background: var(--chip-background-2);
143
- margin-bottom: 16px;
144
-
145
- .page-image {
146
- max-width: 100%;
147
- max-height: 80vh;
148
- object-fit: contain;
149
- }
150
-
151
- .page-text {
152
- padding: 32px;
153
- font-size: 14px;
154
- line-height: 22px;
155
- }
156
- }
157
-
158
- .page-controls {
159
- display: flex;
160
- justify-content: center;
161
- align-items: center;
162
- gap: 24px;
163
-
164
- button {
165
- display: flex;
166
- align-items: center;
167
- gap: 4px;
168
- padding: 8px 16px;
169
- border: 1px solid var(--search-bar-border-color);
170
- background: transparent;
171
- color: var(--card-cover-title);
172
- cursor: pointer;
173
-
174
- &:disabled {
175
- opacity: 0.5;
176
- cursor: default;
177
- }
178
- }
179
-
180
- span {
181
- font-size: 14px;
182
- color: var(--secondary-info-fg);
183
- }
184
- }
185
-
186
- .magazine-pdf {
187
- text-align: center;
188
-
189
- .download-btn {
190
- display: inline-block;
191
- padding: 12px 32px;
192
- border: 1px solid var(--chip-text);
193
- color: var(--chip-text);
194
- text-decoration: none;
195
- font-size: 14px;
196
-
197
- &:hover {
198
- background: var(--chip-text);
199
- color: var(--chip-background-2);
200
- }
201
- }
202
- }
203
-
204
- .loading {
205
- text-align: center;
206
- padding: 64px;
207
- }
208
- }
209
- </style>