@mundogamernetwork/shared-ui 1.0.0

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 (87) hide show
  1. package/README.md +283 -0
  2. package/components/PressKit/AssetGallery.vue +349 -0
  3. package/components/PressKit/Awards.vue +100 -0
  4. package/components/PressKit/Credits.vue +78 -0
  5. package/components/PressKit/FactSheet.vue +204 -0
  6. package/components/PressKit/Hero.vue +143 -0
  7. package/components/PressKit/Quotes.vue +80 -0
  8. package/components/PressKit/VideoPlayer.vue +134 -0
  9. package/components/checkout/MgCartItemList.vue +214 -0
  10. package/components/checkout/MgCartSummary.vue +204 -0
  11. package/components/checkout/MgCheckoutSidebar.vue +230 -0
  12. package/components/checkout/MgGuestEmailForm.vue +97 -0
  13. package/components/checkout/MgPaymentMethodSelector.vue +162 -0
  14. package/components/checkout/MgPixQRCode.vue +222 -0
  15. package/components/indie-wall/IndieWallLeaderboard.vue +208 -0
  16. package/components/indie-wall/MuralCanvas.vue +481 -0
  17. package/components/indie-wall/StepBlock.vue +314 -0
  18. package/components/indie-wall/StepCustomize.vue +530 -0
  19. package/components/indie-wall/StepGoal.vue +169 -0
  20. package/components/indie-wall/StepPackage.vue +145 -0
  21. package/components/indie-wall/StepPay.vue +209 -0
  22. package/components/indie-wall/SupportStepper.vue +372 -0
  23. package/components/invoices/MgInvoiceDownload.vue +50 -0
  24. package/components/pricing/MgBillingToggle.vue +74 -0
  25. package/components/pricing/MgPricingCard.vue +245 -0
  26. package/components/ui/Header/MgMessageCard.vue +147 -0
  27. package/components/ui/Header/MgMessageModal.vue +414 -0
  28. package/components/ui/Header/MgNotificationCard.vue +200 -0
  29. package/components/ui/Header/MgNotificationsModal.vue +125 -0
  30. package/components/ui/MgAnnouncementBanner.vue +147 -0
  31. package/components/ui/MgBanners.vue +23 -0
  32. package/components/ui/MgHeaderComponent.vue +283 -0
  33. package/components/ui/MgHeaderUIConfig.vue +225 -0
  34. package/components/ui/MgHeaderUIUser.vue +301 -0
  35. package/components/ui/MgLoginModal.vue +156 -0
  36. package/components/ui/MgPromotionBanner.vue +185 -0
  37. package/composables/useLogout.ts +42 -0
  38. package/composables/useMgCheckout.ts +287 -0
  39. package/composables/useMgUserNotifications.ts +122 -0
  40. package/composables/usePaymentMethods.ts +75 -0
  41. package/composables/useSubscription.ts +163 -0
  42. package/middleware/auth.global.ts +40 -0
  43. package/nuxt.config.ts +31 -0
  44. package/package.json +40 -0
  45. package/pages/[slug]/index.vue +112 -0
  46. package/pages/about.vue +133 -0
  47. package/pages/blog.vue +430 -0
  48. package/pages/careers.vue +329 -0
  49. package/pages/contact.vue +339 -0
  50. package/pages/faq.vue +317 -0
  51. package/pages/health-check.vue +20 -0
  52. package/pages/icons.vue +58 -0
  53. package/pages/magazine/[slug].vue +209 -0
  54. package/pages/magazine/index.vue +267 -0
  55. package/pages/media-kit/[slug].vue +625 -0
  56. package/pages/mural/[slug].vue +1058 -0
  57. package/pages/partners.vue +290 -0
  58. package/pages/press.vue +237 -0
  59. package/pages/presskit/[slug].vue +191 -0
  60. package/pages/roadmap.vue +355 -0
  61. package/pages/status.vue +199 -0
  62. package/pages/team.vue +266 -0
  63. package/pages/wall/[slug].vue +11 -0
  64. package/plugins/auth.client.ts +17 -0
  65. package/plugins/echo.client.ts +132 -0
  66. package/services/authService.ts +95 -0
  67. package/services/chatService.ts +53 -0
  68. package/services/contactService.ts +35 -0
  69. package/services/documentService.ts +16 -0
  70. package/services/httpService.ts +95 -0
  71. package/services/indieWallService.ts +174 -0
  72. package/services/institutionalService.ts +248 -0
  73. package/services/mediaKitService.ts +51 -0
  74. package/services/notificationsService.ts +20 -0
  75. package/services/pressKitService.ts +55 -0
  76. package/stores/announcement.ts +129 -0
  77. package/stores/auth.ts +86 -0
  78. package/stores/chat.ts +150 -0
  79. package/stores/contact.ts +28 -0
  80. package/stores/document.ts +27 -0
  81. package/stores/index.ts +34 -0
  82. package/stores/institutional.ts +231 -0
  83. package/stores/login.ts +27 -0
  84. package/stores/notifications.ts +133 -0
  85. package/stores/promotion.ts +154 -0
  86. package/types/index.ts +135 -0
  87. package/utils/serialize.ts +29 -0
package/pages/blog.vue ADDED
@@ -0,0 +1,430 @@
1
+ <script setup lang="ts">
2
+ import { storeToRefs } from "pinia";
3
+
4
+ definePageMeta({ layout: "default" });
5
+
6
+ const institutionalStore = useInstitutionalStore();
7
+ const { blog, popularBlog, blogCategory, blogTag } = storeToRefs(institutionalStore);
8
+
9
+ const config = useRuntimeConfig();
10
+ const systemId = config.public.mgSharedUi?.systemId || import.meta.env.VITE_SYSTEM_ID;
11
+
12
+ const currentPage = ref(1);
13
+ const perPage = 8;
14
+ const selectedPeriod = ref("");
15
+ const selectedCategories = ref<number[]>([]);
16
+ const selectedTags = ref<number[]>([]);
17
+
18
+ const periodOptions = ["hour", "day", "week", "month", "year"];
19
+
20
+ async function fetchBlogData() {
21
+ try {
22
+ const params: any = {
23
+ filter: {
24
+ status: 1,
25
+ platform_id: systemId,
26
+ },
27
+ sort: "created_at",
28
+ order: "desc",
29
+ per_page: perPage,
30
+ page: currentPage.value,
31
+ };
32
+
33
+ if (selectedPeriod.value) params.filter.period = selectedPeriod.value;
34
+ if (selectedCategories.value.length > 0) params.filter.category_id = selectedCategories.value;
35
+ if (selectedTags.value.length > 0) params.filter.tag_id = selectedTags.value;
36
+
37
+ await institutionalStore.fetchBlog(params);
38
+ } catch {
39
+ // silent
40
+ }
41
+ }
42
+
43
+ async function fetchPopular() {
44
+ try {
45
+ await institutionalStore.fetchPopularBlog({
46
+ filter: {
47
+ status: 1,
48
+ platform_id: systemId,
49
+ },
50
+ sort: "views",
51
+ order: "desc",
52
+ per_page: 5,
53
+ page: 1,
54
+ });
55
+ } catch {
56
+ // silent
57
+ }
58
+ }
59
+
60
+ async function fetchFilters() {
61
+ try {
62
+ await Promise.all([
63
+ institutionalStore.fetchBlogCategory({ per_page: "all" }),
64
+ institutionalStore.fetchBlogTag({ per_page: "all" }),
65
+ ]);
66
+ } catch {
67
+ // silent
68
+ }
69
+ }
70
+
71
+ function toggleCategory(id: number) {
72
+ const idx = selectedCategories.value.indexOf(id);
73
+ if (idx > -1) selectedCategories.value.splice(idx, 1);
74
+ else selectedCategories.value.push(id);
75
+ currentPage.value = 1;
76
+ fetchBlogData();
77
+ }
78
+
79
+ function toggleTag(id: number) {
80
+ const idx = selectedTags.value.indexOf(id);
81
+ if (idx > -1) selectedTags.value.splice(idx, 1);
82
+ else selectedTags.value.push(id);
83
+ currentPage.value = 1;
84
+ fetchBlogData();
85
+ }
86
+
87
+ function changePeriod() {
88
+ currentPage.value = 1;
89
+ fetchBlogData();
90
+ }
91
+
92
+ function changePage(page: number) {
93
+ currentPage.value = page;
94
+ fetchBlogData();
95
+ }
96
+
97
+ onMounted(() => {
98
+ fetchBlogData();
99
+ fetchPopular();
100
+ fetchFilters();
101
+ });
102
+ </script>
103
+
104
+ <template>
105
+ <div id="blog" class="container blog">
106
+ <section>
107
+ <div class="header">
108
+ <div>
109
+ <h1 class="title-4">{{ $t("more.blog.title") }}</h1>
110
+ <p>{{ $t("more.blog.subtitle") }}</p>
111
+ </div>
112
+ </div>
113
+ <div class="container">
114
+ <div class="row">
115
+ <div class="col-12 col-md-8">
116
+ <div v-if="blog.status === 'pending' && (!blog.data || blog.data.length === 0)" class="loading">
117
+ <p>{{ $t("components.loading") }}</p>
118
+ </div>
119
+
120
+ <div v-else-if="blog.data && blog.data.length > 0">
121
+ <div class="blog-grid">
122
+ <div
123
+ v-for="post in blog.data"
124
+ :key="post.id"
125
+ class="blog-card"
126
+ >
127
+ <img
128
+ v-if="post.cover_image_url"
129
+ :src="post.cover_image_url"
130
+ :alt="post.localized_title"
131
+ class="blog-card__image"
132
+ />
133
+ <div class="blog-card__content">
134
+ <h3>{{ post.localized_title }}</h3>
135
+ <p>{{ post.localized_short_description }}</p>
136
+ <div class="blog-card__meta">
137
+ <span v-if="post.author">{{ post.author.name }}</span>
138
+ <span>{{ post.created_at }}</span>
139
+ <span v-if="post.comments_count">{{ post.comments_count }} {{ $t("more.blog.comments") }}</span>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <div v-if="blog.data.meta" class="pagination-controls">
146
+ <button
147
+ :disabled="currentPage <= 1"
148
+ @click="changePage(currentPage - 1)"
149
+ >
150
+ {{ $t("components.previous") }}
151
+ </button>
152
+ <span>{{ currentPage }} / {{ blog.data.meta.last_page || 1 }}</span>
153
+ <button
154
+ :disabled="currentPage >= (blog.data.meta.last_page || 1)"
155
+ @click="changePage(currentPage + 1)"
156
+ >
157
+ {{ $t("components.next") }}
158
+ </button>
159
+ </div>
160
+ </div>
161
+
162
+ <div v-else class="no-data">
163
+ {{ $t("more.blog.no_data") }}
164
+ </div>
165
+ </div>
166
+
167
+ <div class="col-12 col-md-4">
168
+ <div class="sidebar">
169
+ <div class="sidebar__section">
170
+ <h4>{{ $t("more.blog.filter_period") }}</h4>
171
+ <select v-model="selectedPeriod" @change="changePeriod">
172
+ <option value="">{{ $t("more.blog.all_time") }}</option>
173
+ <option
174
+ v-for="p in periodOptions"
175
+ :key="p"
176
+ :value="p"
177
+ >
178
+ {{ $t(`more.blog.period_${p}`) }}
179
+ </option>
180
+ </select>
181
+ </div>
182
+
183
+ <div class="sidebar__section" v-if="blogCategory?.data?.data?.length">
184
+ <h4>{{ $t("more.blog.categories") }}</h4>
185
+ <div class="tag-list">
186
+ <button
187
+ v-for="cat in blogCategory.data.data"
188
+ :key="cat.id"
189
+ class="tag-btn"
190
+ :class="{ active: selectedCategories.includes(cat.id) }"
191
+ @click="toggleCategory(cat.id)"
192
+ >
193
+ {{ cat.localized_name || cat.name }}
194
+ </button>
195
+ </div>
196
+ </div>
197
+
198
+ <div class="sidebar__section" v-if="blogTag?.data?.data?.length">
199
+ <h4>{{ $t("more.blog.tags") }}</h4>
200
+ <div class="tag-list">
201
+ <button
202
+ v-for="tag in blogTag.data.data"
203
+ :key="tag.id"
204
+ class="tag-btn"
205
+ :class="{ active: selectedTags.includes(tag.id) }"
206
+ @click="toggleTag(tag.id)"
207
+ >
208
+ {{ tag.localized_name || tag.name }}
209
+ </button>
210
+ </div>
211
+ </div>
212
+
213
+ <div class="sidebar__section" v-if="popularBlog?.data?.data?.length">
214
+ <h4>{{ $t("more.blog.most_read") }}</h4>
215
+ <div class="popular-list">
216
+ <div
217
+ v-for="post in popularBlog.data.data"
218
+ :key="post.id"
219
+ class="popular-item"
220
+ >
221
+ <img
222
+ v-if="post.cover_image_url"
223
+ :src="post.cover_image_url"
224
+ :alt="post.localized_title"
225
+ />
226
+ <div>
227
+ <h5>{{ post.localized_title }}</h5>
228
+ <span>{{ post.created_at }}</span>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ </div>
237
+ </section>
238
+ </div>
239
+ </template>
240
+
241
+ <style lang="scss" scoped>
242
+ .blog {
243
+ color: var(--inactive);
244
+ font-size: 0.875rem;
245
+
246
+ .header {
247
+ position: relative;
248
+ height: 200px;
249
+ display: flex;
250
+ justify-content: center;
251
+ align-items: center;
252
+ margin-bottom: 2rem;
253
+
254
+ & > div {
255
+ width: 480px;
256
+ text-align: center;
257
+ }
258
+
259
+ &:before {
260
+ content: " ";
261
+ display: block;
262
+ position: absolute;
263
+ background: url("@/assets/images/bg-blog.png") no-repeat center center / cover;
264
+ width: 100%;
265
+ height: 100%;
266
+ top: 50%;
267
+ transform: translateY(-50%);
268
+ z-index: -1;
269
+ }
270
+ }
271
+
272
+ .blog-grid {
273
+ display: flex;
274
+ flex-direction: column;
275
+ gap: 16px;
276
+ }
277
+
278
+ .blog-card {
279
+ display: flex;
280
+ gap: 16px;
281
+ background-color: var(--chip-background-2);
282
+ padding: 16px;
283
+
284
+ &__image {
285
+ width: 160px;
286
+ height: 120px;
287
+ object-fit: cover;
288
+ flex-shrink: 0;
289
+ }
290
+
291
+ &__content {
292
+ flex: 1;
293
+
294
+ h3 {
295
+ font-size: 16px;
296
+ font-weight: 600;
297
+ color: var(--card-cover-title);
298
+ margin-bottom: 8px;
299
+ }
300
+
301
+ p {
302
+ font-size: 14px;
303
+ line-height: 20px;
304
+ margin-bottom: 8px;
305
+ }
306
+ }
307
+
308
+ &__meta {
309
+ display: flex;
310
+ gap: 12px;
311
+ font-size: 12px;
312
+ color: var(--secondary-info-fg);
313
+ }
314
+ }
315
+
316
+ .sidebar {
317
+ display: flex;
318
+ flex-direction: column;
319
+ gap: 24px;
320
+
321
+ &__section {
322
+ h4 {
323
+ font-size: 14px;
324
+ font-weight: 600;
325
+ color: var(--card-cover-title);
326
+ margin-bottom: 12px;
327
+ }
328
+
329
+ select {
330
+ width: 100%;
331
+ height: 36px;
332
+ padding: 0 8px;
333
+ border: 1px solid var(--search-bar-border-color);
334
+ background: var(--search-bar-bg);
335
+ color: var(--search-bar-fg);
336
+ font-size: 14px;
337
+ }
338
+ }
339
+ }
340
+
341
+ .tag-list {
342
+ display: flex;
343
+ flex-wrap: wrap;
344
+ gap: 8px;
345
+
346
+ .tag-btn {
347
+ padding: 4px 12px;
348
+ border: 1px solid var(--search-bar-border-color);
349
+ background: transparent;
350
+ color: var(--inactive);
351
+ font-size: 12px;
352
+ cursor: pointer;
353
+
354
+ &.active {
355
+ background: var(--chip-text);
356
+ color: var(--chip-background-2);
357
+ border-color: var(--chip-text);
358
+ }
359
+ }
360
+ }
361
+
362
+ .popular-list {
363
+ display: flex;
364
+ flex-direction: column;
365
+ gap: 12px;
366
+
367
+ .popular-item {
368
+ display: flex;
369
+ gap: 8px;
370
+
371
+ img {
372
+ width: 60px;
373
+ height: 45px;
374
+ object-fit: cover;
375
+ flex-shrink: 0;
376
+ }
377
+
378
+ h5 {
379
+ font-size: 13px;
380
+ font-weight: 500;
381
+ color: var(--card-cover-title);
382
+ margin-bottom: 4px;
383
+ }
384
+
385
+ span {
386
+ font-size: 11px;
387
+ color: var(--secondary-info-fg);
388
+ }
389
+ }
390
+ }
391
+
392
+ .pagination-controls {
393
+ display: flex;
394
+ justify-content: center;
395
+ align-items: center;
396
+ margin-top: 20px;
397
+ gap: 16px;
398
+
399
+ button {
400
+ padding: 8px 15px;
401
+ background-color: var(--chip-background-2);
402
+ color: var(--card-cover-title);
403
+ border: 1px solid var(--search-bar-border-color);
404
+ cursor: pointer;
405
+
406
+ &:disabled {
407
+ opacity: 0.5;
408
+ cursor: default;
409
+ }
410
+ }
411
+ }
412
+
413
+ .no-data {
414
+ text-align: center;
415
+ padding: 32px;
416
+ color: var(--secondary-info-fg);
417
+ }
418
+ }
419
+
420
+ @media screen and (max-width: 768px) {
421
+ .blog .blog-card {
422
+ flex-direction: column;
423
+
424
+ &__image {
425
+ width: 100%;
426
+ height: 180px;
427
+ }
428
+ }
429
+ }
430
+ </style>