@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,145 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ tiers: any[]
4
+ selectedTierId: number | null
5
+ customQuantity: number
6
+ pixelPrice: number
7
+ currency: string
8
+ }>()
9
+
10
+ const emit = defineEmits<{
11
+ (e: 'select-tier', tierId: number | null, pixelCount: number): void
12
+ (e: 'set-custom', qty: number): void
13
+ (e: 'next'): void
14
+ }>()
15
+
16
+ const localQty = ref(props.customQuantity || 1)
17
+
18
+ watch(() => props.customQuantity, (v) => { localQty.value = v })
19
+
20
+ function pickTier(tier: any) {
21
+ emit('select-tier', tier.id, tier.pixel_count)
22
+ emit('next')
23
+ }
24
+
25
+ function pickCustom() {
26
+ const qty = Math.max(1, Math.floor(localQty.value || 1))
27
+ emit('set-custom', qty)
28
+ emit('select-tier', null, qty)
29
+ emit('next')
30
+ }
31
+
32
+ function tierUnavailable(tier: any) {
33
+ return tier.max_quantity && tier.sold_count >= tier.max_quantity
34
+ }
35
+ </script>
36
+
37
+ <template>
38
+ <div class="sw-step">
39
+ <h3 class="sw-step-title">{{ $t('tv.dashboard.indie_wall.step_package_title') }}</h3>
40
+ <p class="sw-step-help">{{ $t('tv.dashboard.indie_wall.step_package_help') }}</p>
41
+
42
+ <div class="sw-grid">
43
+ <button
44
+ v-for="tier in tiers"
45
+ :key="tier.id"
46
+ class="sw-card"
47
+ type="button"
48
+ :class="{ active: selectedTierId === tier.id }"
49
+ :disabled="tierUnavailable(tier)"
50
+ @click="pickTier(tier)"
51
+ >
52
+ <img v-if="tier.image_url" :src="tier.image_url" alt="" class="sw-tier-img" />
53
+ <span class="sw-card-title">{{ tier.name }}</span>
54
+ <span class="sw-card-price">{{ currency }} {{ Number(tier.price).toFixed(2) }}</span>
55
+ <span class="sw-card-pixels">{{ tier.pixel_count }} {{ $t('tv.dashboard.indie_wall.spots_unit') }}</span>
56
+ <p v-if="tier.description" class="sw-card-desc">{{ tier.description }}</p>
57
+ <span v-if="tier.max_quantity" class="sw-card-stock">
58
+ {{ tier.sold_count }}/{{ tier.max_quantity }} {{ $t('tv.dashboard.indie_wall.sold') }}
59
+ </span>
60
+ </button>
61
+
62
+ <div class="sw-card sw-custom" :class="{ active: selectedTierId === null && customQuantity > 0 }">
63
+ <span class="sw-card-title">{{ $t('tv.dashboard.indie_wall.step_package_custom_title') }}</span>
64
+ <p class="sw-card-desc">{{ $t('tv.dashboard.indie_wall.step_package_custom_desc') }}</p>
65
+ <div class="sw-custom-row">
66
+ <input v-model.number="localQty" type="number" min="1" class="sw-custom-input" />
67
+ <span class="sw-custom-x">×</span>
68
+ <span class="sw-custom-price">{{ currency }} {{ Number(pixelPrice).toFixed(2) }}</span>
69
+ </div>
70
+ <div class="sw-custom-total">
71
+ = {{ currency }} {{ (Math.max(1, localQty) * pixelPrice).toFixed(2) }}
72
+ </div>
73
+ <button type="button" class="sw-custom-cta" @click="pickCustom">
74
+ {{ $t('tv.dashboard.indie_wall.step_package_custom_cta') }}
75
+ </button>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </template>
80
+
81
+ <style scoped lang="scss">
82
+ .sw-step { display: flex; flex-direction: column; gap: 12px; }
83
+ .sw-step-title { font-size: 1.05rem; font-weight: 600; color: var(--title-fg, #fff); margin: 0; }
84
+ .sw-step-help { font-size: 0.82rem; color: var(--secondary-info-fg, #aaa); margin: 0 0 4px; }
85
+
86
+ .sw-grid {
87
+ display: grid;
88
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
89
+ gap: 8px;
90
+ }
91
+
92
+ .sw-card {
93
+ background: #191B20;
94
+ border: 1px solid #1e2028;
95
+ padding: 14px;
96
+ text-align: left;
97
+ cursor: pointer;
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: 6px;
101
+ color: inherit;
102
+ font-family: inherit;
103
+ transition: border-color 0.15s, background 0.15s;
104
+
105
+ &:hover:not(:disabled) {
106
+ border-color: var(--chip-text, #D297FF);
107
+ background: #1e2028;
108
+ }
109
+
110
+ &.active { border-color: var(--chip-text, #D297FF); background: #1e2028; }
111
+ &:disabled { opacity: 0.45; cursor: not-allowed; }
112
+ }
113
+
114
+ .sw-tier-img { width: 100%; height: 80px; object-fit: cover; }
115
+ .sw-card-title { font-size: 0.95rem; font-weight: 600; color: var(--title-fg, #fff); }
116
+ .sw-card-price { font-size: 1.1rem; font-weight: 700; color: var(--chip-text, #D297FF); }
117
+ .sw-card-pixels { font-size: 0.75rem; color: var(--secondary-info-fg, #888); }
118
+ .sw-card-desc { color: var(--secondary-info-fg, #888); font-size: 0.78rem; margin: 0; line-height: 1.4; }
119
+ .sw-card-stock { color: var(--secondary-info-fg, #888); font-size: 0.7rem; }
120
+
121
+ .sw-custom { border-style: dashed; }
122
+ .sw-custom-row { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
123
+ .sw-custom-input {
124
+ width: 70px; height: 32px;
125
+ background: #13161C; border: 1px solid #272930; color: var(--title-fg, #fff);
126
+ text-align: center; font-size: 0.85rem;
127
+ -moz-appearance: textfield;
128
+ &::-webkit-outer-spin-button, &::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
129
+ &:focus { border-color: var(--chip-text, #D297FF); outline: none; }
130
+ }
131
+ .sw-custom-x { color: var(--secondary-info-fg, #555); }
132
+ .sw-custom-price { color: var(--secondary-info-fg, #888); font-size: 0.85rem; }
133
+ .sw-custom-total { color: var(--chip-text, #D297FF); font-weight: 700; font-size: 0.95rem; }
134
+ .sw-custom-cta {
135
+ background: var(--chip-text, #D297FF);
136
+ color: #13161C;
137
+ border: none;
138
+ padding: 8px 12px;
139
+ font-weight: 700;
140
+ font-size: 0.82rem;
141
+ cursor: pointer;
142
+ margin-top: 4px;
143
+ &:hover { opacity: 0.88; }
144
+ }
145
+ </style>
@@ -0,0 +1,209 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ wall: any
4
+ selectedGoal: any | null
5
+ selectedTier: any | null
6
+ selection: { x: number; y: number; w: number; h: number } | null
7
+ form: {
8
+ title: string
9
+ message: string
10
+ background_color: string
11
+ image_url: string
12
+ link: string
13
+ is_anonymous: boolean
14
+ }
15
+ pixelCount: number
16
+ gatewayProvider: 'stripe' | 'paypal'
17
+ processing: boolean
18
+ errorMsg: string
19
+ }>()
20
+
21
+ const emit = defineEmits<{
22
+ (e: 'set-gateway', provider: 'stripe' | 'paypal'): void
23
+ (e: 'confirm'): void
24
+ (e: 'cancel'): void
25
+ }>()
26
+
27
+ const total = computed(() => {
28
+ if (props.selectedTier) return Number(props.selectedTier.price ?? 0)
29
+ return props.pixelCount * Number(props.wall?.pixel_price ?? 0)
30
+ })
31
+ const agreed = ref(false)
32
+ const agreedRights = ref(false)
33
+ </script>
34
+
35
+ <template>
36
+ <div class="sw-step">
37
+ <h3 class="sw-step-title">{{ $t('tv.dashboard.indie_wall.step_pay_title') }}</h3>
38
+
39
+ <div class="sw-summary">
40
+ <div class="sw-summary-row">
41
+ <span class="sw-summary-label">{{ $t('tv.dashboard.indie_wall.goals') }}</span>
42
+ <span class="sw-summary-val">{{ selectedGoal ? selectedGoal.title : $t('tv.dashboard.indie_wall.step_goal_skip_title') }}</span>
43
+ </div>
44
+ <div class="sw-summary-row">
45
+ <span class="sw-summary-label">{{ $t('tv.dashboard.indie_wall.tiers') }}</span>
46
+ <span class="sw-summary-val">
47
+ {{ selectedTier ? selectedTier.name : $t('tv.dashboard.indie_wall.step_package_custom_title') }}
48
+ · {{ pixelCount }} {{ $t('tv.dashboard.indie_wall.spots_unit') }}
49
+ </span>
50
+ </div>
51
+ <div v-if="selection" class="sw-summary-row">
52
+ <span class="sw-summary-label">{{ $t('tv.dashboard.indie_wall.mural_position') }}</span>
53
+ <span class="sw-summary-val">({{ selection.x }}, {{ selection.y }}) — {{ selection.w }}×{{ selection.h }}</span>
54
+ </div>
55
+ <div class="sw-summary-row sw-summary-preview">
56
+ <div class="sw-swatch" :style="form.image_url ? '' : { background: form.background_color }">
57
+ <img v-if="form.image_url" :src="form.image_url" alt="" />
58
+ </div>
59
+ <div class="sw-preview-text">
60
+ <strong>{{ form.is_anonymous ? $t('tv.dashboard.indie_wall.anonymous') : (form.title || '—') }}</strong>
61
+ <span v-if="form.message">"{{ form.message }}"</span>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <fieldset class="sw-pay-fieldset">
67
+ <legend>{{ $t('tv.dashboard.indie_wall.step_pay_method') }}</legend>
68
+ <label class="sw-pay-option" :class="{ active: gatewayProvider === 'stripe' }">
69
+ <input
70
+ type="radio"
71
+ name="gateway"
72
+ value="stripe"
73
+ :checked="gatewayProvider === 'stripe'"
74
+ @change="emit('set-gateway', 'stripe')"
75
+ />
76
+ <span class="sw-pay-name">{{ $t('tv.dashboard.indie_wall.pay_stripe') }}</span>
77
+ <span class="sw-pay-meta">{{ $t('tv.dashboard.indie_wall.pay_stripe_meta') }}</span>
78
+ </label>
79
+ <label class="sw-pay-option" :class="{ active: gatewayProvider === 'paypal' }">
80
+ <input
81
+ type="radio"
82
+ name="gateway"
83
+ value="paypal"
84
+ :checked="gatewayProvider === 'paypal'"
85
+ @change="emit('set-gateway', 'paypal')"
86
+ />
87
+ <span class="sw-pay-name">{{ $t('tv.dashboard.indie_wall.pay_paypal') }}</span>
88
+ <span class="sw-pay-meta">{{ $t('tv.dashboard.indie_wall.pay_paypal_meta') }}</span>
89
+ </label>
90
+ <label class="sw-pay-option sw-pay-disabled">
91
+ <input type="radio" name="gateway" value="pix" disabled />
92
+ <span class="sw-pay-name">{{ $t('tv.dashboard.indie_wall.pay_pix') }}</span>
93
+ <span class="sw-pay-meta">{{ $t('tv.dashboard.indie_wall.pay_pix_soon') }}</span>
94
+ </label>
95
+ </fieldset>
96
+
97
+ <div class="sw-total">
98
+ <span>{{ $t('tv.dashboard.indie_wall.step_pay_total') }}</span>
99
+ <strong>{{ wall?.currency }} {{ total.toFixed(2) }}</strong>
100
+ </div>
101
+
102
+ <label class="sw-agree">
103
+ <input v-model="agreed" type="checkbox" />
104
+ <span>{{ $t('tv.dashboard.indie_wall.pay_agree') }}</span>
105
+ </label>
106
+
107
+ <label class="sw-agree">
108
+ <input v-model="agreedRights" type="checkbox" />
109
+ <span>{{ $t('tv.dashboard.indie_wall.pay_agree_rights') }}</span>
110
+ </label>
111
+
112
+ <div v-if="errorMsg" class="sw-error">{{ errorMsg }}</div>
113
+
114
+ <div class="sw-actions">
115
+ <button type="button" class="sw-cancel" :disabled="processing" @click="emit('cancel')">
116
+ {{ $t('tv.dashboard.indie_wall.cancel') }}
117
+ </button>
118
+ <button
119
+ type="button"
120
+ class="sw-confirm"
121
+ :disabled="processing || !agreed || !agreedRights"
122
+ @click="emit('confirm')"
123
+ >
124
+ {{ processing ? '…' : $t('tv.dashboard.indie_wall.confirm_support') }}
125
+ </button>
126
+ </div>
127
+ </div>
128
+ </template>
129
+
130
+ <style scoped lang="scss">
131
+ .sw-step { display: flex; flex-direction: column; gap: 10px; }
132
+ .sw-step-title { font-size: 1.05rem; font-weight: 600; color: var(--title-fg, #fff); margin: 0; }
133
+
134
+ .sw-summary {
135
+ background: #13161C; border: 1px solid #1e2028;
136
+ padding: 12px; display: flex; flex-direction: column; gap: 6px;
137
+ }
138
+ .sw-summary-row { display: flex; justify-content: space-between; gap: 8px; font-size: 0.82rem; }
139
+ .sw-summary-label { color: var(--secondary-info-fg, #777); }
140
+ .sw-summary-val { color: var(--title-fg, #fff); }
141
+ .sw-summary-preview { align-items: center; gap: 10px; padding-top: 6px; border-top: 1px solid #1e2028; }
142
+ .sw-swatch {
143
+ width: 32px; height: 32px; flex-shrink: 0; overflow: hidden;
144
+ img { width: 100%; height: 100%; object-fit: cover; image-rendering: pixelated; }
145
+ }
146
+ .sw-preview-text { display: flex; flex-direction: column; flex: 1; }
147
+ .sw-preview-text strong { color: var(--title-fg, #fff); font-size: 0.85rem; }
148
+ .sw-preview-text span { color: var(--secondary-info-fg, #888); font-size: 0.78rem; font-style: italic; }
149
+
150
+ .sw-pay-fieldset {
151
+ border: none; padding: 0; margin: 0;
152
+ display: flex; flex-direction: column; gap: 6px;
153
+ legend {
154
+ font-size: 0.78rem; color: var(--secondary-info-fg, #aaa);
155
+ padding: 0; margin-bottom: 6px;
156
+ }
157
+ }
158
+ .sw-pay-option {
159
+ display: grid;
160
+ grid-template-columns: 18px 1fr auto;
161
+ align-items: center;
162
+ gap: 10px;
163
+ background: #1e2028; border: 1px solid #272930;
164
+ padding: 10px 12px; cursor: pointer;
165
+ transition: border-color 0.15s, background 0.15s;
166
+ &:hover:not(.sw-pay-disabled) { border-color: var(--chip-text, #D297FF); }
167
+ &.active {
168
+ border-color: var(--chip-text, #D297FF);
169
+ background: #272930;
170
+ }
171
+ &.sw-pay-disabled { opacity: 0.5; cursor: not-allowed; }
172
+ input { accent-color: var(--chip-text, #D297FF); }
173
+ }
174
+ .sw-pay-name { font-size: 0.88rem; color: var(--title-fg, #fff); font-weight: 600; }
175
+ .sw-pay-meta { font-size: 0.72rem; color: var(--secondary-info-fg, #888); }
176
+
177
+ .sw-total {
178
+ display: flex; justify-content: space-between; align-items: center;
179
+ padding: 10px 12px; background: #1e2028; border: 1px solid #272930;
180
+ font-size: 0.85rem; color: var(--secondary-info-fg, #888);
181
+ strong { color: var(--chip-text, #D297FF); font-size: 1.2rem; }
182
+ }
183
+
184
+ .sw-error {
185
+ background: rgba(255,59,48,0.07);
186
+ border: 1px solid rgba(255,59,48,0.25);
187
+ color: #ff6b6b; padding: 8px 10px; font-size: 0.8rem;
188
+ }
189
+
190
+ .sw-agree {
191
+ display: flex; align-items: flex-start; gap: 8px; cursor: pointer;
192
+ font-size: 0.78rem; color: var(--secondary-info-fg, #aaa); line-height: 1.4;
193
+ input[type="checkbox"] { flex-shrink: 0; margin-top: 2px; accent-color: var(--chip-text, #D297FF); }
194
+ }
195
+
196
+ .sw-actions { display: flex; gap: 8px; }
197
+ .sw-cancel {
198
+ flex: 1; background: #1e2028; color: var(--secondary-info-fg, #888);
199
+ border: 1px solid #272930; padding: 10px; cursor: pointer; font-size: 0.85rem;
200
+ &:hover { color: var(--title-fg, #fff); }
201
+ }
202
+ .sw-confirm {
203
+ flex: 1; background: var(--chip-text, #D297FF);
204
+ color: #13161C; border: none; padding: 10px;
205
+ font-weight: 700; font-size: 0.85rem; cursor: pointer;
206
+ &:hover { opacity: 0.88; }
207
+ &:disabled { opacity: 0.4; cursor: not-allowed; }
208
+ }
209
+ </style>