@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
@@ -0,0 +1,245 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import type { Plan, PlanPricing } from "../../composables/useSubscription";
4
+
5
+ const props = defineProps<{
6
+ plan: Plan;
7
+ billingPeriod: "monthly" | "annual";
8
+ currentPlanSlug?: string | null;
9
+ paymentGateway?: string;
10
+ monthlyPeriodId?: number;
11
+ annualPeriodId?: number;
12
+ currencySymbol?: string;
13
+ locale?: string;
14
+ }>();
15
+
16
+ const emit = defineEmits<{
17
+ subscribe: [pricingId: number, planSlug: string];
18
+ contact: [];
19
+ }>();
20
+
21
+ const MONTHLY_ID = props.monthlyPeriodId ?? 4;
22
+ const ANNUAL_ID = props.annualPeriodId ?? 7;
23
+
24
+ const pricing = computed<PlanPricing | undefined>(() => {
25
+ const pricings = props.plan.plan_pricings?.data ?? [];
26
+ const periodId = props.billingPeriod === "monthly" ? MONTHLY_ID : ANNUAL_ID;
27
+ return pricings.find(
28
+ (p) => (p.payment_period?.id ?? p.payment_period_id) === periodId
29
+ );
30
+ });
31
+
32
+ const isCurrentPlan = computed(
33
+ () => props.currentPlanSlug === props.plan.slug
34
+ );
35
+
36
+ const isEnterprise = computed(() => props.plan.trial_days === 0);
37
+
38
+ const formattedPrice = computed(() => {
39
+ if (!pricing.value) return "";
40
+ const val = pricing.value.value;
41
+ if (isEnterprise.value && val === 0) return null;
42
+ const symbol = props.currencySymbol ?? pricing.value.currency?.symbol ?? "$";
43
+ return `${symbol}${Number(val).toFixed(2)}`;
44
+ });
45
+
46
+ const periodLabel = computed(() => {
47
+ if (props.billingPeriod === "annual") return "/yr";
48
+ return "/mo";
49
+ });
50
+
51
+ function handleCta() {
52
+ if (isEnterprise.value) {
53
+ emit("contact");
54
+ return;
55
+ }
56
+ if (pricing.value) {
57
+ emit("subscribe", pricing.value.id, props.plan.slug);
58
+ }
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <div
64
+ class="mg-pricing-card"
65
+ :class="{
66
+ 'mg-pricing-card--popular': plan.popular,
67
+ 'mg-pricing-card--current': isCurrentPlan,
68
+ }"
69
+ >
70
+ <div v-if="plan.popular" class="mg-pricing-card__badge">
71
+ {{ $t?.("pricing.popular") ?? "Most Popular" }}
72
+ </div>
73
+
74
+ <h3 class="mg-pricing-card__name">{{ plan.name }}</h3>
75
+
76
+ <p v-if="plan.description" class="mg-pricing-card__description">
77
+ {{ plan.description }}
78
+ </p>
79
+
80
+ <div class="mg-pricing-card__price">
81
+ <template v-if="formattedPrice">
82
+ <span class="price-value">{{ formattedPrice }}</span>
83
+ <span class="price-period">{{ periodLabel }}</span>
84
+ </template>
85
+ <template v-else>
86
+ <span class="price-custom">{{ $t?.("pricing.custom") ?? "Custom" }}</span>
87
+ </template>
88
+ </div>
89
+
90
+ <div v-if="plan.trial_days && plan.trial_days > 0" class="mg-pricing-card__trial">
91
+ {{ $t?.("pricing.trial", { days: plan.trial_days }) ?? `${plan.trial_days}-day free trial` }}
92
+ </div>
93
+
94
+ <button
95
+ class="mg-pricing-card__cta"
96
+ :class="{ 'mg-pricing-card__cta--current': isCurrentPlan }"
97
+ :disabled="isCurrentPlan"
98
+ @click="handleCta"
99
+ >
100
+ <template v-if="isCurrentPlan">
101
+ {{ $t?.("pricing.current_plan") ?? "Current Plan" }}
102
+ </template>
103
+ <template v-else-if="isEnterprise">
104
+ {{ $t?.("pricing.contact_sales") ?? "Contact Sales" }}
105
+ </template>
106
+ <template v-else>
107
+ {{ $t?.("pricing.subscribe") ?? "Subscribe Now" }}
108
+ </template>
109
+ </button>
110
+
111
+ <div v-if="plan.plan_benefits?.data?.length" class="mg-pricing-card__features">
112
+ <div
113
+ v-for="benefit in plan.plan_benefits.data"
114
+ :key="benefit.id"
115
+ class="feature-item"
116
+ >
117
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2.5">
118
+ <polyline points="20 6 9 17 4 12" />
119
+ </svg>
120
+ <span>{{ benefit.localized_name ?? benefit.name }}</span>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </template>
125
+
126
+ <style lang="scss" scoped>
127
+ .mg-pricing-card {
128
+ display: flex;
129
+ flex-direction: column;
130
+ padding: 32px 24px;
131
+ border: 1px solid var(--inactive, #e5e7eb);
132
+ border-radius: 12px;
133
+ background: var(--body-bg-card, #fff);
134
+ position: relative;
135
+ transition: border-color 0.2s;
136
+
137
+ &:hover {
138
+ border-color: var(--active, #9ca3af);
139
+ }
140
+
141
+ &--popular {
142
+ border-color: #4f46e5;
143
+ box-shadow: 0 4px 20px rgba(79, 70, 229, 0.1);
144
+ }
145
+
146
+ &--current {
147
+ border-color: #22c55e;
148
+ }
149
+
150
+ &__badge {
151
+ position: absolute;
152
+ top: -12px;
153
+ left: 50%;
154
+ transform: translateX(-50%);
155
+ padding: 4px 16px;
156
+ background: #4f46e5;
157
+ color: #fff;
158
+ font-size: 11px;
159
+ font-weight: 600;
160
+ border-radius: 12px;
161
+ white-space: nowrap;
162
+ }
163
+
164
+ &__name {
165
+ font-size: 20px;
166
+ font-weight: 700;
167
+ color: var(--active, #111827);
168
+ margin: 0 0 8px;
169
+ }
170
+
171
+ &__description {
172
+ font-size: 13px;
173
+ color: var(--inactive, #6b7280);
174
+ line-height: 1.5;
175
+ margin: 0 0 20px;
176
+ }
177
+
178
+ &__price {
179
+ display: flex;
180
+ align-items: baseline;
181
+ gap: 4px;
182
+ margin-bottom: 4px;
183
+ }
184
+
185
+ &__trial {
186
+ font-size: 12px;
187
+ color: #22c55e;
188
+ margin-bottom: 20px;
189
+ }
190
+
191
+ &__cta {
192
+ width: 100%;
193
+ height: 44px;
194
+ border: none;
195
+ border-radius: 8px;
196
+ background: #4f46e5;
197
+ color: #fff;
198
+ font-size: 14px;
199
+ font-weight: 600;
200
+ cursor: pointer;
201
+ transition: background 0.2s;
202
+ margin-bottom: 24px;
203
+
204
+ &:hover:not(:disabled) { background: #4338ca; }
205
+ &:disabled { opacity: 0.5; cursor: not-allowed; }
206
+
207
+ &--current {
208
+ background: #22c55e;
209
+ }
210
+ }
211
+
212
+ &__features {
213
+ display: flex;
214
+ flex-direction: column;
215
+ gap: 10px;
216
+ }
217
+ }
218
+
219
+ .price-value {
220
+ font-size: 36px;
221
+ font-weight: 800;
222
+ color: var(--active, #111827);
223
+ }
224
+
225
+ .price-period {
226
+ font-size: 14px;
227
+ color: var(--inactive, #6b7280);
228
+ }
229
+
230
+ .price-custom {
231
+ font-size: 24px;
232
+ font-weight: 700;
233
+ color: var(--active, #111827);
234
+ }
235
+
236
+ .feature-item {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: 8px;
240
+ font-size: 13px;
241
+ color: var(--active, #374151);
242
+
243
+ svg { flex-shrink: 0; }
244
+ }
245
+ </style>
@@ -0,0 +1,147 @@
1
+ <template>
2
+ <article class="message-card" :class="{ read: chat.unread_messages_count === 0 }">
3
+ <div class="infos">
4
+ <div class="align-circle" @click="openChat()">
5
+ <img :src="getUserAvatar" alt="" width="48" height="48" />
6
+ <div class="online-circle" :class="getShowStatus()"></div>
7
+ </div>
8
+ <div class="description" @click="openChat()">
9
+ <div class="title">{{ getName }}</div>
10
+ <p>
11
+ {{ isAuthor ? $t("message_component.message_time_ago_you") : $t("message_component.message_time_ago") }}
12
+ {{ getTimeAgo }}
13
+ </p>
14
+ </div>
15
+ <div class="action-bar">
16
+ <div class="unread-circle" v-if="chat.unread_messages_count > 0"></div>
17
+ </div>
18
+ </div>
19
+ </article>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import { ref, computed } from "vue";
24
+ import type { MgUserStatus } from "../../../types";
25
+
26
+ const props = defineProps({
27
+ chat: {
28
+ type: Object,
29
+ required: true,
30
+ default: () => ({}),
31
+ },
32
+ });
33
+
34
+ const getUserAvatar = computed(() => {
35
+ return props.chat?.users?.[0]?.avatar_url || "/imgs/default.jpg";
36
+ });
37
+
38
+ const getName = computed(() => {
39
+ return props.chat?.users?.[0]?.nickname || "";
40
+ });
41
+
42
+ const getTimeAgo = computed(() => {
43
+ return props.chat?.last_message?.created_at_diff || "";
44
+ });
45
+
46
+ const isAuthor = computed(() => {
47
+ return props.chat?.last_message?.user_is_author || false;
48
+ });
49
+
50
+ const getShowStatus = (): string => {
51
+ const status: MgUserStatus | undefined = props.chat?.users?.[0]?.user_status?.[0];
52
+ if (!status || status.hiddenstatus) return "offline";
53
+ return status.online ? "online" : "offline";
54
+ };
55
+
56
+ const emit = defineEmits(["show-message"]);
57
+
58
+ const openChat = () => {
59
+ emit("show-message", props.chat.id);
60
+ };
61
+ </script>
62
+
63
+ <style lang="scss" scoped>
64
+ .message-card {
65
+ display: flex;
66
+ padding: 8px;
67
+ align-items: center;
68
+ gap: 8px;
69
+ cursor: pointer;
70
+ background-color: var(--header-ui-config-hover);
71
+
72
+ &.read {
73
+ background-color: var(--chat-bg, var(--card-article-bg));
74
+ }
75
+
76
+ .infos {
77
+ display: flex;
78
+ gap: 8px;
79
+ flex: 1 0 0;
80
+ align-items: center;
81
+ padding: 8px;
82
+
83
+ .align-circle {
84
+ position: relative;
85
+
86
+ img {
87
+ max-width: 48px;
88
+ height: 48px;
89
+ object-fit: cover;
90
+ border-radius: 100%;
91
+ }
92
+
93
+ .online-circle {
94
+ width: 12px;
95
+ height: 12px;
96
+ border-radius: 100%;
97
+ position: absolute;
98
+ right: 4px;
99
+ bottom: 0;
100
+ border: 3px solid var(--body-bg-card);
101
+
102
+ &.online {
103
+ background-color: #42ff00;
104
+ }
105
+
106
+ &.offline {
107
+ background-color: #ff0000;
108
+ }
109
+ }
110
+ }
111
+
112
+ .description {
113
+ font-size: 14px;
114
+ width: 100%;
115
+ display: flex;
116
+ flex-direction: column;
117
+ padding: 8px;
118
+
119
+ .title {
120
+ font-size: 14px;
121
+ font-weight: 400;
122
+ color: var(--text-primary-color);
123
+ }
124
+
125
+ p {
126
+ font-size: 12px;
127
+ color: #aaa;
128
+ margin-bottom: 0;
129
+ }
130
+ }
131
+
132
+ .action-bar {
133
+ display: flex;
134
+ flex-direction: column;
135
+ align-items: center;
136
+
137
+ .unread-circle {
138
+ width: 8px;
139
+ height: 8px;
140
+ background-color: var(--highlight-color);
141
+ border-radius: 100%;
142
+ margin: 8px;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ </style>