@mundogamernetwork/shared-ui 1.1.37 → 1.1.39

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.
@@ -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>