@mundogamernetwork/shared-ui 1.17.5 → 1.17.7

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.
@@ -43,6 +43,7 @@
43
43
  />
44
44
  <div class="card-checkbox-content">
45
45
  <span class="card-name">{{ card.content_format?.name || card.label }}</span>
46
+ <span v-if="card.platform?.name" class="card-platform">{{ card.platform.name }}</span>
46
47
  <span class="card-channel">{{ card.ad_channel?.name }}</span>
47
48
  <span class="card-price">{{ formatPrice(card.price_min, card.currency) }}</span>
48
49
  </div>
@@ -144,7 +145,7 @@
144
145
  <p v-if="bundle.description" class="bundle-desc">{{ bundle.description }}</p>
145
146
  <div class="bundle-items">
146
147
  <span v-for="item in bundle.rate_cards?.data || bundle.rate_cards || []" :key="item.id" class="bundle-item-tag">
147
- {{ item.content_format?.name || item.label }}
148
+ {{ item.content_format?.name || item.label }}<template v-if="item.platform?.name"> · {{ item.platform.name }}</template>
148
149
  </span>
149
150
  </div>
150
151
  <div class="bundle-pricing">
@@ -183,6 +184,7 @@ interface RateCard {
183
184
  description?: string;
184
185
  content_format?: { id: number; name: string };
185
186
  ad_channel?: { id: number; name: string };
187
+ platform?: { id: number; name: string };
186
188
  }
187
189
 
188
190
  interface Bundle {
@@ -563,6 +565,14 @@ async function removeBundle(id: number) {
563
565
  flex: 1;
564
566
 
565
567
  .card-name { color: #FFF; font-size: 13px; font-weight: 600; }
568
+ .card-platform {
569
+ background: rgba(253, 178, 21, 0.12);
570
+ color: #FDB215;
571
+ padding: 2px 8px;
572
+ border-radius: 20px;
573
+ font-size: 11px;
574
+ font-weight: 500;
575
+ }
566
576
  .card-channel { color: #888; font-size: 12px; }
567
577
  .card-price { color: #FDB215; font-size: 13px; font-weight: 600; margin-left: auto; }
568
578
  }
@@ -32,6 +32,15 @@
32
32
  </option>
33
33
  </select>
34
34
  </div>
35
+ <div class="form-group">
36
+ <label>{{ $t("media_kit.editor.platform") }}</label>
37
+ <select v-model="form.platform_id">
38
+ <option value="">{{ $t("media_kit.editor.select") }}</option>
39
+ <option v-for="platform in platforms" :key="platform.id" :value="platform.id">
40
+ {{ platform.name }}
41
+ </option>
42
+ </select>
43
+ </div>
35
44
  </div>
36
45
  <div class="form-row">
37
46
  <div class="form-group">
@@ -111,6 +120,7 @@
111
120
  <div class="card-info">
112
121
  <span class="format">{{ card.content_format?.name || card.label }}</span>
113
122
  <span class="channel">{{ card.ad_channel?.name }}</span>
123
+ <span v-if="card.platform?.name" class="platform-tag">{{ card.platform.name }}</span>
114
124
  <span class="price">{{ formatPrice(card.price_min, card.price_max, card.currency) }}</span>
115
125
  </div>
116
126
  <!-- Two-step delete rather than a modal: this component ships to
@@ -150,6 +160,7 @@ interface RateCard {
150
160
  id: number;
151
161
  content_format_id: number;
152
162
  ad_channel_id: number;
163
+ platform_id?: number | null;
153
164
  label?: string;
154
165
  price_min: string;
155
166
  price_max?: string;
@@ -159,6 +170,7 @@ interface RateCard {
159
170
  is_active: boolean;
160
171
  content_format?: { id: number; name: string };
161
172
  ad_channel?: { id: number; name: string };
173
+ platform?: { id: number; name: string };
162
174
  }
163
175
 
164
176
  interface LookupItem {
@@ -172,6 +184,7 @@ const props = withDefaults(
172
184
  influencerProInfoId: number;
173
185
  contentFormats: LookupItem[];
174
186
  adChannels: LookupItem[];
187
+ platforms?: LookupItem[];
175
188
  isEnabled?: boolean;
176
189
  /** MGN commission % for the current plan (host app resolves this, e.g. from feature_limits). */
177
190
  commissionPercent?: number;
@@ -184,6 +197,7 @@ const props = withDefaults(
184
197
  }>(),
185
198
  {
186
199
  isEnabled: true,
200
+ platforms: () => [],
187
201
  commissionPercent: 20,
188
202
  planName: "Free",
189
203
  isSubscriber: false,
@@ -212,6 +226,7 @@ const saving = ref(false);
212
226
  const defaultForm = {
213
227
  content_format_id: '',
214
228
  ad_channel_id: '',
229
+ platform_id: '',
215
230
  price_min: '',
216
231
  price_max: '',
217
232
  currency: 'USD',
@@ -281,6 +296,7 @@ const editCard = (card: RateCard) => {
281
296
  form.value = {
282
297
  content_format_id: String(card.content_format_id),
283
298
  ad_channel_id: String(card.ad_channel_id),
299
+ platform_id: card.platform_id ? String(card.platform_id) : '',
284
300
  price_min: card.price_min,
285
301
  price_max: card.price_max || '',
286
302
  currency: card.currency,
@@ -309,6 +325,7 @@ const saveRateCard = async () => {
309
325
  influencer_professional_info_id: props.influencerProInfoId,
310
326
  content_format_id: Number(form.value.content_format_id),
311
327
  ad_channel_id: Number(form.value.ad_channel_id),
328
+ platform_id: form.value.platform_id ? Number(form.value.platform_id) : null,
312
329
  price_min: Number(form.value.price_min),
313
330
  price_max: form.value.price_max ? Number(form.value.price_max) : null,
314
331
  delivery_time_days: form.value.delivery_time_days ? Number(form.value.delivery_time_days) : null,
@@ -594,6 +611,15 @@ const netDisplay = computed(() => {
594
611
  font-size: 12px;
595
612
  }
596
613
 
614
+ .platform-tag {
615
+ background: rgba(253, 178, 21, 0.12);
616
+ color: #FDB215;
617
+ padding: 2px 8px;
618
+ border-radius: 20px;
619
+ font-size: 11px;
620
+ font-weight: 500;
621
+ }
622
+
597
623
  .price {
598
624
  color: #FDB215;
599
625
  font-size: 14px;
@@ -46,6 +46,11 @@ const isAuthenticated = ref(true) // assume true; set false on 401
46
46
  const requestForms = ref<Record<number, { platform_ids: number[]; description: string }>>({})
47
47
  const revealingId = ref<number | null>(null)
48
48
  const revealResult = ref<RevealResult | null>(null)
49
+ // revealingId resets to null in the reveal() finally block, before the modal
50
+ // ever renders (it only shows once revealResult is set) — this holds onto
51
+ // the id long enough for the modal's "send your coverage" link to build
52
+ // /key-campaigns/key-materials?requestId=.
53
+ const revealedRequestId = ref<number | null>(null)
49
54
 
50
55
  const notifPromptVisible = ref(false)
51
56
  const notifSaving = ref(false)
@@ -219,6 +224,7 @@ async function revealCode(reqId: number) {
219
224
  if (props.accessToken) body.access_token = props.accessToken
220
225
  const res = await apiRevealKey(reqId, body)
221
226
  revealResult.value = (res as any)?.data?.data ?? null
227
+ if (revealResult.value) revealedRequestId.value = reqId
222
228
  } catch (err: any) {
223
229
  if (err?.response?.status === 401) isAuthenticated.value = false
224
230
  } finally {
@@ -228,6 +234,7 @@ async function revealCode(reqId: number) {
228
234
 
229
235
  function closeReveal() {
230
236
  revealResult.value = null
237
+ revealedRequestId.value = null
231
238
  }
232
239
 
233
240
  // ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -274,6 +281,7 @@ function statusColor(slug: string): string {
274
281
  :code="revealResult.code"
275
282
  :platform="revealResult.platform"
276
283
  :revealed-at="revealResult.revealed_at"
284
+ :key-request-id="revealedRequestId"
277
285
  @close="closeReveal"
278
286
  />
279
287
 
@@ -25,6 +25,7 @@ const props = defineProps<{
25
25
  streamer_type?: number | null;
26
26
  type?: string | null;
27
27
  platforms?: Array<{ id: number; name: string; slug?: string | null; alternative_name?: string | null } | string>;
28
+ networkSystems?: string[];
28
29
  };
29
30
  publicMode?: boolean;
30
31
  baseRoute?: string;
@@ -46,7 +47,7 @@ const locale = $i18n.locale;
46
47
 
47
48
  const cardCover = computed(() => {
48
49
  const c = props.card as any;
49
- return c.cover || c.cover_src || c.game?.url_cover_src || c.game_cover || "/imgs/no-cover-img.jpg";
50
+ return c.cover || c.cover_src || c.game?.url_cover_src || c.game?.cover_url || c.game?.url_cover || c.game_cover || "/imgs/no-cover-img.jpg";
50
51
  });
51
52
 
52
53
  // Only apply the campaign's own focal point to its own cover — a game-cover/no-cover
@@ -202,6 +203,9 @@ const detailRoute = computed(() =>
202
203
  </div>
203
204
 
204
205
  <div class="kc-card__body">
206
+ <div v-if="card.networkSystems?.length" class="kc-card__networks">
207
+ <span v-for="network in card.networkSystems" :key="network" class="kc-card__network-tag">{{ network }}</span>
208
+ </div>
205
209
  <div v-if="card.platforms?.length" class="kc-card__platforms">
206
210
  <span v-for="p in card.platforms" :key="typeof p === 'string' ? p : p.id" class="kc-card__platform-tag">
207
211
  {{ platformAbbr(p) }}
@@ -295,6 +299,23 @@ const detailRoute = computed(() =>
295
299
  text-align: center;
296
300
  }
297
301
 
302
+ &__networks {
303
+ display: flex;
304
+ flex-wrap: wrap;
305
+ gap: 4px;
306
+ margin-bottom: 6px;
307
+ }
308
+
309
+ &__network-tag {
310
+ border: 1px solid color-mix(in srgb, var(--primary, #D297FF) 55%, transparent);
311
+ border-radius: 999px;
312
+ padding: 2px 7px;
313
+ color: var(--primary, #D297FF);
314
+ font-size: 0.68rem;
315
+ line-height: 1.2;
316
+ white-space: nowrap;
317
+ }
318
+
298
319
  &__exclusive-badge {
299
320
  position: absolute;
300
321
  bottom: 6px;
@@ -7,13 +7,22 @@ const props = defineProps<{
7
7
  code: string
8
8
  platform: string | null
9
9
  revealedAt: string
10
+ // Absent for reveal paths outside the request flow (e.g. an admin grant
11
+ // with no key_request row) — the CTA below only renders when present.
12
+ keyRequestId?: number | null
10
13
  }>()
11
14
 
12
15
  const emit = defineEmits<{ (e: 'close'): void }>()
13
16
 
17
+ const { locale } = useI18n()
18
+
14
19
  function copyCode() {
15
20
  navigator.clipboard?.writeText(props.code)
16
21
  }
22
+
23
+ const materialsUrl = computed(() =>
24
+ props.keyRequestId ? `/${locale.value}/key-campaigns/key-materials?requestId=${props.keyRequestId}` : null
25
+ )
17
26
  </script>
18
27
 
19
28
  <template>
@@ -34,6 +43,10 @@ function copyCode() {
34
43
 
35
44
  <p class="krm__warning">{{ $t('keys.reveal.warning') }}</p>
36
45
 
46
+ <a v-if="materialsUrl" :href="materialsUrl" class="krm__materials-btn">
47
+ {{ $t('keys.reveal.submit_materials_cta') }}
48
+ </a>
49
+
37
50
  <button class="krm__close-btn" @click="emit('close')">
38
51
  {{ $t('common.close') }}
39
52
  </button>
@@ -127,8 +140,21 @@ function copyCode() {
127
140
  margin: 0;
128
141
  }
129
142
 
130
- &__close-btn {
143
+ &__materials-btn {
131
144
  margin-top: 6px;
145
+ padding: 10px 28px;
146
+ background: var(--accent, #00d4ff);
147
+ border: 1px solid var(--accent, #00d4ff);
148
+ color: #000;
149
+ cursor: pointer;
150
+ font-size: 0.9rem;
151
+ font-weight: 600;
152
+ text-decoration: none;
153
+ transition: opacity 0.15s;
154
+ &:hover { opacity: 0.85; }
155
+ }
156
+
157
+ &__close-btn {
132
158
  padding: 10px 28px;
133
159
  background: var(--surface-raised, #1e1e1e);
134
160
  border: 1px solid var(--border-color, #2a2a2a);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mundogamernetwork/shared-ui",
3
- "version": "1.17.5",
3
+ "version": "1.17.7",
4
4
  "description": "Mundo Gamer Network - Shared UI Layer (Nuxt 3)",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -622,7 +622,7 @@ async function loadFeaturedCampaigns() {
622
622
  .filter((item: any) => (truthyFlag(item.featured) || truthyFlag(item.highlight)) && truthyFlag(item.is_open))
623
623
  .map((item: any) => ({
624
624
  id: item.id,
625
- cover: item.cover_src || item.game?.url_banner_src || item.game?.url_cover_src || "/imgs/no-cover-img.jpg",
625
+ cover: item.cover_src || item.game?.url_banner_src || item.game?.url_cover_src || item.game?.cover_url || item.game?.url_cover || "/imgs/no-cover-img.jpg",
626
626
  coverFocalX: item.cover_src ? (item.cover_focal_x ?? 0.5) : 0.5,
627
627
  coverFocalY: item.cover_src ? (item.cover_focal_y ?? 0.5) : 0.5,
628
628
  name: item.name,
@@ -682,7 +682,7 @@ async function loadCampaigns(page = 1) {
682
682
  id: item.id,
683
683
  type: item.type?.name,
684
684
  typeId: item.type?.id,
685
- cover: item.cover_src || item.game?.url_cover_src || item.game?.url_banner_src || "/imgs/no-cover-img.jpg",
685
+ cover: item.cover_src || item.game?.url_cover_src || item.game?.cover_url || item.game?.url_cover || item.game?.url_banner_src || "/imgs/no-cover-img.jpg",
686
686
  coverFocalX: item.cover_src ? (item.cover_focal_x ?? 0.5) : 0.5,
687
687
  coverFocalY: item.cover_src ? (item.cover_focal_y ?? 0.5) : 0.5,
688
688
  text: item.name,
@@ -700,6 +700,7 @@ async function loadCampaigns(page = 1) {
700
700
  available_at_for_user: item.available_at_for_user || null,
701
701
  max_keys_for_user: item.max_keys_for_user || 1,
702
702
  platforms: (item.platforms || []).map((p: any) => p.name),
703
+ networkSystems: (item.mg_network_systems || []).map((s: any) => s.name).filter(Boolean),
703
704
  gameName: item.game?.name || null,
704
705
  genres: (item.game?.genres || []).map((g: any) => g.name).filter(Boolean).slice(0, 2),
705
706
  studioName: item.company?.name || null,
@@ -600,7 +600,7 @@ useHead(() => ({
600
600
 
601
601
  <section v-if="videos.length" id="trailers" class="kit-block">
602
602
  <h2>{{ $t('kit.press.videos') }}</h2>
603
- <PressKitVideoPlayer :videos="videos" />
603
+ <VideoPlayer :videos="videos" />
604
604
  </section>
605
605
 
606
606
  <section v-if="screenshots.length || brandAssets.length" id="media" class="kit-block">
@@ -683,12 +683,12 @@ useHead(() => ({
683
683
 
684
684
  <section v-if="awards.length" id="awards" class="kit-block">
685
685
  <h2>{{ $t('kit.press.awards') }}</h2>
686
- <PressKitAwards :awards="awards" />
686
+ <Awards :awards="awards" />
687
687
  </section>
688
688
 
689
689
  <section v-if="quotes.length" class="kit-block">
690
690
  <h2>{{ $t('kit.press.press') }}</h2>
691
- <PressKitQuotes :quotes="quotes" />
691
+ <Quotes :quotes="quotes" />
692
692
  </section>
693
693
 
694
694
  <!-- Créditos -> Equipe -->
@@ -374,7 +374,7 @@ export const fetchCampaignBySlug = async (slug: string) => {
374
374
  cover: k.cover_src,
375
375
  cover_focal_x: k.cover_focal_x ?? 0.5,
376
376
  cover_focal_y: k.cover_focal_y ?? 0.5,
377
- game_cover: k.game?.url_cover_src,
377
+ game_cover: k.game?.url_cover_src || k.game?.cover_url || k.game?.url_cover,
378
378
  game_slug: k.game?.slug,
379
379
  game_name: k.game?.name ?? null,
380
380
  // Creator feedback. The can_* flags are computed server-side by
@@ -99,3 +99,5 @@ export const fetchMediaKitContentFormats = () =>
99
99
  mk.get("media-kit-lookups/content-formats")
100
100
  export const fetchMediaKitAdChannels = () =>
101
101
  mk.get("media-kit-lookups/ad-channels")
102
+ export const fetchMediaKitPlatforms = () =>
103
+ mk.get("media-kit-lookups/platforms")