@mundogamernetwork/shared-ui 1.1.82 → 1.1.84
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.
- package/locales/de.json +19 -2
- package/locales/en.json +19 -2
- package/locales/pt-BR.json +19 -2
- package/locales/ro.json +19 -2
- package/package.json +1 -1
- package/pages/key-campaigns/[slug].vue +148 -2
- package/pages/key-campaigns/index.vue +8 -1
- package/pages/key-campaigns/redeem-key-approved.vue +248 -127
- package/pages/key-campaigns/redeem-key.vue +19 -7
- package/services/campaignService.ts +3 -0
- package/services/gamesService.ts +8 -0
package/locales/de.json
CHANGED
|
@@ -135,7 +135,8 @@
|
|
|
135
135
|
"no_data": "Campaign not found",
|
|
136
136
|
"title": "Kampagne",
|
|
137
137
|
"about": "Über das Spiel",
|
|
138
|
-
"screenshot_alt": "Spiel-Screenshot"
|
|
138
|
+
"screenshot_alt": "Spiel-Screenshot",
|
|
139
|
+
"see_all": "alle anzeigen"
|
|
139
140
|
},
|
|
140
141
|
"other_campaigns": "Andere Kampagnen",
|
|
141
142
|
"available": "verfügbar",
|
|
@@ -184,7 +185,23 @@
|
|
|
184
185
|
"status_completed": "Anfrage abgeschlossen",
|
|
185
186
|
"status_refused": "Anfrage abgelehnt",
|
|
186
187
|
"status_cancelled": "Anfrage storniert",
|
|
187
|
-
"refused_reason": "Grund der Ablehnung:"
|
|
188
|
+
"refused_reason": "Grund der Ablehnung:",
|
|
189
|
+
"good": "Super!",
|
|
190
|
+
"good_text": "Du hast einen Code zum Einlösen erhalten",
|
|
191
|
+
"approved_subtitle": "Folge den Schritten und löse deinen Schlüssel ein.",
|
|
192
|
+
"cody_text": "Der Code kann nur einmal eingelöst werden und läuft nach Ende des Angebots ab",
|
|
193
|
+
"redeem_description_1": "Öffne die Website oder App der entsprechenden Plattform (Steam, PlayStation, Xbox usw.) und melde dich bei deinem Konto an.",
|
|
194
|
+
"redeem_description_2": "Gehe zur Option „Code einlösen“, füge den kopierten Code ein und schließe den Vorgang ab.",
|
|
195
|
+
"redeem_description_3": "Der Inhalt wird auf deinem Konto aktiviert.",
|
|
196
|
+
"redeem_at": "Auf {platform} einlösen",
|
|
197
|
+
"no_redeemers": "Noch niemand hat diese Kampagne eingelöst.",
|
|
198
|
+
"required_fields": "Bitte alle Pflichtfelder ausfüllen",
|
|
199
|
+
"plan": "Deckungspläne",
|
|
200
|
+
"plan_placeholder": "Beschreibe deinen Berichterstattungsplan: wo du über das Spiel streamen oder posten wirst, dein Publikum und das geplante Veröffentlichungsdatum",
|
|
201
|
+
"resources_title": "Ressourcen",
|
|
202
|
+
"press_kit_link": "Presse-Kit",
|
|
203
|
+
"review_guideline": "Review-Richtlinie",
|
|
204
|
+
"embargo_date": "Sperrfrist"
|
|
188
205
|
},
|
|
189
206
|
"materials": {
|
|
190
207
|
"title": "Submit material",
|
package/locales/en.json
CHANGED
|
@@ -135,7 +135,8 @@
|
|
|
135
135
|
"no_data": "Campaign not found",
|
|
136
136
|
"title": "Campaign",
|
|
137
137
|
"about": "About the game",
|
|
138
|
-
"screenshot_alt": "Game screenshot"
|
|
138
|
+
"screenshot_alt": "Game screenshot",
|
|
139
|
+
"see_all": "see all"
|
|
139
140
|
},
|
|
140
141
|
"other_campaigns": "Other campaigns",
|
|
141
142
|
"available": "available",
|
|
@@ -184,7 +185,23 @@
|
|
|
184
185
|
"status_completed": "Request completed",
|
|
185
186
|
"status_refused": "Request refused",
|
|
186
187
|
"status_cancelled": "Request cancelled",
|
|
187
|
-
"refused_reason": "Reason for refusal:"
|
|
188
|
+
"refused_reason": "Reason for refusal:",
|
|
189
|
+
"good": "Nice!",
|
|
190
|
+
"good_text": "You received a code to redeem",
|
|
191
|
+
"approved_subtitle": "Follow the steps and redeem your key.",
|
|
192
|
+
"cody_text": "The code can only be redeemed once and expires after the offer ends",
|
|
193
|
+
"redeem_description_1": "Open the website or app of the corresponding platform (Steam, PlayStation, Xbox, etc.) and log in to your account.",
|
|
194
|
+
"redeem_description_2": "Go to the 'Redeem code' option, paste the copied code, and complete the process.",
|
|
195
|
+
"redeem_description_3": "The content will be activated on your account.",
|
|
196
|
+
"redeem_at": "Redeem on {platform}",
|
|
197
|
+
"no_redeemers": "No one has redeemed this campaign yet.",
|
|
198
|
+
"required_fields": "Please fill in all required fields",
|
|
199
|
+
"plan": "Coverage plans",
|
|
200
|
+
"plan_placeholder": "Describe your coverage plan: where you will stream or post about this game, your audience, and expected publish date",
|
|
201
|
+
"resources_title": "Resources",
|
|
202
|
+
"press_kit_link": "Press Kit",
|
|
203
|
+
"review_guideline": "Review guideline",
|
|
204
|
+
"embargo_date": "Embargo date"
|
|
188
205
|
},
|
|
189
206
|
"materials": {
|
|
190
207
|
"title": "Submit material",
|
package/locales/pt-BR.json
CHANGED
|
@@ -135,7 +135,8 @@
|
|
|
135
135
|
"no_data": "Campaign not found",
|
|
136
136
|
"title": "Campanha",
|
|
137
137
|
"about": "Sobre o jogo",
|
|
138
|
-
"screenshot_alt": "Captura de tela do jogo"
|
|
138
|
+
"screenshot_alt": "Captura de tela do jogo",
|
|
139
|
+
"see_all": "ver todos"
|
|
139
140
|
},
|
|
140
141
|
"other_campaigns": "Outras campanhas",
|
|
141
142
|
"available": "disponível",
|
|
@@ -184,7 +185,23 @@
|
|
|
184
185
|
"status_completed": "Solicitação concluída",
|
|
185
186
|
"status_refused": "Solicitação recusada",
|
|
186
187
|
"status_cancelled": "Solicitação cancelada",
|
|
187
|
-
"refused_reason": "Motivo da recusa:"
|
|
188
|
+
"refused_reason": "Motivo da recusa:",
|
|
189
|
+
"good": "Boa!",
|
|
190
|
+
"good_text": "Você recebeu um código para resgatar",
|
|
191
|
+
"approved_subtitle": "Siga as etapas e resgate sua chave.",
|
|
192
|
+
"cody_text": "O código só pode ser resgatado uma vez e expira após o término da oferta",
|
|
193
|
+
"redeem_description_1": "Abra o site ou app da plataforma correspondente (Steam, PlayStation, Xbox, etc.) e faça login na sua conta.",
|
|
194
|
+
"redeem_description_2": " Vá até a opção “Resgatar código”, cole o código copiado e finalize.",
|
|
195
|
+
"redeem_description_3": "O conteúdo será ativado na sua conta.",
|
|
196
|
+
"redeem_at": "Resgatar em {platform}",
|
|
197
|
+
"no_redeemers": "Nenhum criador resgatou esta campanha ainda.",
|
|
198
|
+
"required_fields": "Por favor, preencha todos os campos obrigatórios",
|
|
199
|
+
"plan": " Planos de cobertura",
|
|
200
|
+
"plan_placeholder": "Descreva seu plano de cobertura: onde vai fazer stream ou publicar sobre o jogo, seu público e a data prevista de publicação",
|
|
201
|
+
"resources_title": "Recursos",
|
|
202
|
+
"press_kit_link": "Press Kit",
|
|
203
|
+
"review_guideline": "Diretriz de review",
|
|
204
|
+
"embargo_date": "Data de embargo"
|
|
188
205
|
},
|
|
189
206
|
"materials": {
|
|
190
207
|
"title": "Submit material",
|
package/locales/ro.json
CHANGED
|
@@ -135,7 +135,8 @@
|
|
|
135
135
|
"no_data": "Campaign not found",
|
|
136
136
|
"title": "Campanie",
|
|
137
137
|
"about": "Despre joc",
|
|
138
|
-
"screenshot_alt": "Captură de ecran a jocului"
|
|
138
|
+
"screenshot_alt": "Captură de ecran a jocului",
|
|
139
|
+
"see_all": "vezi toate"
|
|
139
140
|
},
|
|
140
141
|
"other_campaigns": "Alte campanii",
|
|
141
142
|
"available": "disponibil",
|
|
@@ -184,7 +185,23 @@
|
|
|
184
185
|
"status_completed": "Cerere finalizată",
|
|
185
186
|
"status_refused": "Cerere refuzată",
|
|
186
187
|
"status_cancelled": "Cerere anulată",
|
|
187
|
-
"refused_reason": "Motivul refuzului:"
|
|
188
|
+
"refused_reason": "Motivul refuzului:",
|
|
189
|
+
"good": "Super!",
|
|
190
|
+
"good_text": "Ai primit un cod de revendicare",
|
|
191
|
+
"approved_subtitle": "Urmează pașii și revendică-ți cheia.",
|
|
192
|
+
"cody_text": "Codul poate fi folosit o singură dată și expiră la finalul ofertei",
|
|
193
|
+
"redeem_description_1": "Deschide site-ul sau aplicația platformei corespunzătoare (Steam, PlayStation, Xbox etc.) și autentifică-te în contul tău.",
|
|
194
|
+
"redeem_description_2": "Accesează opțiunea „Redeem code”, lipește codul copiat și finalizează procesul.",
|
|
195
|
+
"redeem_description_3": "Conținutul va fi activat în contul tău.",
|
|
196
|
+
"redeem_at": "Revendică pe {platform}",
|
|
197
|
+
"no_redeemers": "Nimeni nu a revendicat această campanie încă.",
|
|
198
|
+
"required_fields": "Vă rugăm să completați toate câmpurile obligatorii",
|
|
199
|
+
"plan": "Planuri de acoperire",
|
|
200
|
+
"plan_placeholder": "Descrie planul tău de acoperire: unde vei face stream sau vei posta despre joc, publicul tău și data estimată de publicare",
|
|
201
|
+
"resources_title": "Resurse",
|
|
202
|
+
"press_kit_link": "Press Kit",
|
|
203
|
+
"review_guideline": "Ghid de recenzie",
|
|
204
|
+
"embargo_date": "Data embargo"
|
|
188
205
|
},
|
|
189
206
|
"materials": {
|
|
190
207
|
"title": "Submit material",
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
revealKey,
|
|
11
11
|
requestKey,
|
|
12
12
|
} from "../../services/campaignService"
|
|
13
|
-
import { fetchDetailGameImage } from "../../services/gamesService"
|
|
13
|
+
import { fetchDetailGame, fetchDetailGameImage } from "../../services/gamesService"
|
|
14
14
|
|
|
15
15
|
// Injected by each front — provide in app.vue / a plugin
|
|
16
16
|
const mgPlatform = inject<string>("mgPlatform", "MGTV")
|
|
@@ -136,6 +136,20 @@ const { data: gameDetailsImages } = await useAsyncData(
|
|
|
136
136
|
{ watch: [campaign] },
|
|
137
137
|
)
|
|
138
138
|
|
|
139
|
+
const { data: gameDetails } = await useAsyncData(
|
|
140
|
+
`campaign-game-detail-${slug}`,
|
|
141
|
+
async () => {
|
|
142
|
+
if (!campaign.value?.game_slug) return null
|
|
143
|
+
try {
|
|
144
|
+
const response = await fetchDetailGame(locale.value, campaign.value.game_slug)
|
|
145
|
+
return response.data.data || null
|
|
146
|
+
} catch {
|
|
147
|
+
return null
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
{ watch: [campaign] },
|
|
151
|
+
)
|
|
152
|
+
|
|
139
153
|
const showVideoModal = ref(false)
|
|
140
154
|
const selectedVideoUrl = ref("")
|
|
141
155
|
const showImageModal = ref(false)
|
|
@@ -590,7 +604,9 @@ onMounted(async () => {
|
|
|
590
604
|
</div>
|
|
591
605
|
</div>
|
|
592
606
|
|
|
593
|
-
<div class="
|
|
607
|
+
<div v-if="!campaign" class="state error">{{ $t("keys.campaigns.error_loading") }}</div>
|
|
608
|
+
|
|
609
|
+
<div v-else class="content-key">
|
|
594
610
|
<div class="content-key-header">
|
|
595
611
|
<h4>{{ campaign?.name }}</h4>
|
|
596
612
|
|
|
@@ -600,6 +616,17 @@ onMounted(async () => {
|
|
|
600
616
|
{{ b }}
|
|
601
617
|
</div>
|
|
602
618
|
</div>
|
|
619
|
+
|
|
620
|
+
<div class="tags-items" v-if="gameDetails?.platforms?.length">
|
|
621
|
+
<div class="tag" v-for="platform in gameDetails.platforms" :key="platform.id">
|
|
622
|
+
{{ platform.name }}
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
<div class="tags-items" v-if="gameDetails?.genres?.length">
|
|
626
|
+
<div class="tag" v-for="genre in gameDetails.genres" :key="genre.id">
|
|
627
|
+
{{ genre.localized_name }}
|
|
628
|
+
</div>
|
|
629
|
+
</div>
|
|
603
630
|
</div>
|
|
604
631
|
|
|
605
632
|
<!-- Cover when no game details -->
|
|
@@ -804,6 +831,36 @@ onMounted(async () => {
|
|
|
804
831
|
<span>+{{ redeemedUsers?.total ?? 0 }}</span>
|
|
805
832
|
{{ $t("keys.campaigns.creators") }}
|
|
806
833
|
</div>
|
|
834
|
+
<div class="see" v-if="(redeemedUsers?.users?.length ?? 0) > 0" @click="showUsersModal = true">
|
|
835
|
+
{{ $t("keys.campaigns.campaign.see_all") }}
|
|
836
|
+
</div>
|
|
837
|
+
</div>
|
|
838
|
+
</div>
|
|
839
|
+
|
|
840
|
+
<div class="users-modal" v-if="showUsersModal" @click.self="showUsersModal = false">
|
|
841
|
+
<div class="users-modal-content">
|
|
842
|
+
<div class="users-modal-header">
|
|
843
|
+
<span>{{ $t("keys.campaigns.campaign.who_redeem") }}</span>
|
|
844
|
+
<button class="close-button" @click="showUsersModal = false">
|
|
845
|
+
<MGIcon size="1x" icon="close" />
|
|
846
|
+
</button>
|
|
847
|
+
</div>
|
|
848
|
+
<div class="users-modal-body">
|
|
849
|
+
<div
|
|
850
|
+
v-if="!redeemedUsers?.users?.length"
|
|
851
|
+
class="users-modal-empty"
|
|
852
|
+
>
|
|
853
|
+
{{ $t("keys.campaigns.no_redeemers") }}
|
|
854
|
+
</div>
|
|
855
|
+
<div v-for="(u, index) in redeemedUsers?.users" :key="index" class="users-modal-row">
|
|
856
|
+
<img
|
|
857
|
+
:src="u.avatar_url || '/imgs/avatar-default.jpg'"
|
|
858
|
+
:alt="u.nickname"
|
|
859
|
+
@error="(e: Event) => { const t = e.target as HTMLImageElement; if (t && t.src !== '/imgs/avatar-default.jpg') t.src = '/imgs/avatar-default.jpg' }"
|
|
860
|
+
/>
|
|
861
|
+
<span>{{ u.nickname || `${u.first_name || ''} ${u.last_name || ''}`.trim() }}</span>
|
|
862
|
+
</div>
|
|
863
|
+
</div>
|
|
807
864
|
</div>
|
|
808
865
|
</div>
|
|
809
866
|
|
|
@@ -976,6 +1033,13 @@ onMounted(async () => {
|
|
|
976
1033
|
img { width: 100%; height: 400px; object-fit: cover; object-position: center top; }
|
|
977
1034
|
}
|
|
978
1035
|
|
|
1036
|
+
.state.error {
|
|
1037
|
+
padding: 64px 0;
|
|
1038
|
+
text-align: center;
|
|
1039
|
+
font-size: 14px;
|
|
1040
|
+
color: var(--danger, #e53935);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
979
1043
|
.content-key {
|
|
980
1044
|
display: flex;
|
|
981
1045
|
flex-direction: column;
|
|
@@ -1008,6 +1072,24 @@ onMounted(async () => {
|
|
|
1008
1072
|
i { font-size: 13px; }
|
|
1009
1073
|
}
|
|
1010
1074
|
}
|
|
1075
|
+
|
|
1076
|
+
.tags-items {
|
|
1077
|
+
display: flex;
|
|
1078
|
+
gap: 4px;
|
|
1079
|
+
flex-flow: wrap;
|
|
1080
|
+
|
|
1081
|
+
.tag {
|
|
1082
|
+
height: 32px;
|
|
1083
|
+
display: flex;
|
|
1084
|
+
font-size: 12px;
|
|
1085
|
+
color: var(--secondary-info-fg);
|
|
1086
|
+
font-weight: 400;
|
|
1087
|
+
background-color: var(--button-secondary-default-bg);
|
|
1088
|
+
justify-content: center;
|
|
1089
|
+
padding: 12px;
|
|
1090
|
+
align-items: center;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1011
1093
|
}
|
|
1012
1094
|
|
|
1013
1095
|
.cards {
|
|
@@ -1306,6 +1388,70 @@ onMounted(async () => {
|
|
|
1306
1388
|
width: -webkit-fill-available;
|
|
1307
1389
|
span { font-size: 16px; color: var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF)))); font-weight: 600; }
|
|
1308
1390
|
}
|
|
1391
|
+
|
|
1392
|
+
.see {
|
|
1393
|
+
font-size: 12px;
|
|
1394
|
+
color: var(--key-accent, var(--primary, #D297FF));
|
|
1395
|
+
cursor: pointer;
|
|
1396
|
+
text-decoration: underline;
|
|
1397
|
+
white-space: nowrap;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
.users-modal {
|
|
1402
|
+
position: fixed;
|
|
1403
|
+
inset: 0;
|
|
1404
|
+
z-index: 1000;
|
|
1405
|
+
display: flex;
|
|
1406
|
+
align-items: center;
|
|
1407
|
+
justify-content: center;
|
|
1408
|
+
background: var(--overlay-dark-strong, rgba(0, 0, 0, 0.75));
|
|
1409
|
+
|
|
1410
|
+
.users-modal-content {
|
|
1411
|
+
width: 100%;
|
|
1412
|
+
max-width: 360px;
|
|
1413
|
+
max-height: 70vh;
|
|
1414
|
+
background: var(--button-secondary-default-bg);
|
|
1415
|
+
display: flex;
|
|
1416
|
+
flex-direction: column;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
.users-modal-header {
|
|
1420
|
+
display: flex;
|
|
1421
|
+
align-items: center;
|
|
1422
|
+
justify-content: space-between;
|
|
1423
|
+
padding: 16px;
|
|
1424
|
+
font-size: 14px;
|
|
1425
|
+
font-weight: 600;
|
|
1426
|
+
color: var(--card-article-title);
|
|
1427
|
+
border-bottom: 1px solid var(--bg-app-badge);
|
|
1428
|
+
|
|
1429
|
+
.close-button { background: transparent; border: none; color: var(--secondary-info-fg); cursor: pointer; }
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
.users-modal-body {
|
|
1433
|
+
overflow-y: auto;
|
|
1434
|
+
padding: 8px 16px;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
.users-modal-empty {
|
|
1438
|
+
padding: 24px 0;
|
|
1439
|
+
text-align: center;
|
|
1440
|
+
font-size: 13px;
|
|
1441
|
+
color: var(--secondary-info-fg);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
.users-modal-row {
|
|
1445
|
+
display: flex;
|
|
1446
|
+
align-items: center;
|
|
1447
|
+
gap: 10px;
|
|
1448
|
+
padding: 10px 0;
|
|
1449
|
+
border-bottom: 1px solid var(--bg-app-badge);
|
|
1450
|
+
&:last-child { border-bottom: none; }
|
|
1451
|
+
|
|
1452
|
+
img { width: 36px; height: 36px; border-radius: 0; }
|
|
1453
|
+
span { font-size: 13px; color: var(--card-article-title); }
|
|
1454
|
+
}
|
|
1309
1455
|
}
|
|
1310
1456
|
|
|
1311
1457
|
.company {
|
|
@@ -267,7 +267,11 @@
|
|
|
267
267
|
</div>
|
|
268
268
|
</div>
|
|
269
269
|
|
|
270
|
-
<div v-if="!loading &&
|
|
270
|
+
<div v-if="!loading && loadError" class="d-flex flex-column align-items-center">
|
|
271
|
+
<div class="no-content"><MGIcon icon="eye-slash" /></div>
|
|
272
|
+
<h4>{{ $t("keys.campaigns.error_loading") }}</h4>
|
|
273
|
+
</div>
|
|
274
|
+
<div v-else-if="!loading && filteredCards.length === 0" class="d-flex flex-column align-items-center">
|
|
271
275
|
<div class="no-content"><MGIcon icon="eye-slash" /></div>
|
|
272
276
|
<h4>{{ $t("keys.campaigns.no_results") }}</h4>
|
|
273
277
|
</div>
|
|
@@ -364,6 +368,7 @@ let slideInterval: ReturnType<typeof setInterval> | null = null
|
|
|
364
368
|
const loading = ref(true)
|
|
365
369
|
const loadingFeatured = ref(true)
|
|
366
370
|
const loadingRequests = ref(false)
|
|
371
|
+
const loadError = ref(false)
|
|
367
372
|
|
|
368
373
|
// ------------------------------------------------------------------
|
|
369
374
|
// Filters
|
|
@@ -556,9 +561,11 @@ async function loadCampaigns(page = 1) {
|
|
|
556
561
|
}
|
|
557
562
|
|
|
558
563
|
currentPage.value = page
|
|
564
|
+
loadError.value = false
|
|
559
565
|
applyUserRequestStatuses()
|
|
560
566
|
} catch {
|
|
561
567
|
hasMoreItems.value = false
|
|
568
|
+
if (page === 1) loadError.value = true
|
|
562
569
|
} finally {
|
|
563
570
|
loading.value = false
|
|
564
571
|
loadingMore.value = false
|
|
@@ -20,6 +20,31 @@ const keyCode = ref<string | null>(null)
|
|
|
20
20
|
const error = ref("")
|
|
21
21
|
const requestData = ref<any>(null)
|
|
22
22
|
const copied = ref(false)
|
|
23
|
+
const platform = ref("")
|
|
24
|
+
|
|
25
|
+
// Maps a platform name to its official key-activation page.
|
|
26
|
+
const REDEEM_URLS: Record<string, string> = {
|
|
27
|
+
steam: "https://store.steampowered.com/account/registerkey",
|
|
28
|
+
playstation: "https://store.playstation.com/redeem",
|
|
29
|
+
"playstation 4": "https://store.playstation.com/redeem",
|
|
30
|
+
"playstation 5": "https://store.playstation.com/redeem",
|
|
31
|
+
xbox: "https://redeem.microsoft.com",
|
|
32
|
+
"xbox one": "https://redeem.microsoft.com",
|
|
33
|
+
"xbox series x/s": "https://redeem.microsoft.com",
|
|
34
|
+
"pc (microsoft windows)": "https://store.steampowered.com/account/registerkey",
|
|
35
|
+
epic: "https://store.epicgames.com/redeem",
|
|
36
|
+
"epic games": "https://store.epicgames.com/redeem",
|
|
37
|
+
gog: "https://www.gog.com/redeem",
|
|
38
|
+
nintendo: "https://ecommerce.nintendo.com/redeem",
|
|
39
|
+
"nintendo switch": "https://ecommerce.nintendo.com/redeem",
|
|
40
|
+
"google stadia": "https://stadia.google.com/redeem",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const redeemUrl = computed(() => {
|
|
44
|
+
const key = (platform.value || "").trim().toLowerCase()
|
|
45
|
+
if (!key) return ""
|
|
46
|
+
return REDEEM_URLS[key] || ""
|
|
47
|
+
})
|
|
23
48
|
|
|
24
49
|
onMounted(async () => {
|
|
25
50
|
if (!currentUser) {
|
|
@@ -34,6 +59,9 @@ onMounted(async () => {
|
|
|
34
59
|
try {
|
|
35
60
|
const res = await fetchKeyRequestById(requestId)
|
|
36
61
|
requestData.value = res?.data?.data || res?.data || null
|
|
62
|
+
// Platform the user chose at request time — shown as a fallback/preview
|
|
63
|
+
// even before reveal, in case the assigned key item has no platform set.
|
|
64
|
+
platform.value = requestData.value?.platforms?.[0]?.name || ""
|
|
37
65
|
} catch {
|
|
38
66
|
error.value = $i18n.t("keys.campaigns.error_loading")
|
|
39
67
|
} finally {
|
|
@@ -47,7 +75,10 @@ async function reveal() {
|
|
|
47
75
|
try {
|
|
48
76
|
const res = await revealKey(requestId)
|
|
49
77
|
const data = res?.data?.data || res?.data || null
|
|
50
|
-
keyCode.value = data?.
|
|
78
|
+
keyCode.value = data?.code || (typeof data?.key === "string" ? data.key : null) || data?.value || null
|
|
79
|
+
// Keep the pre-reveal platform (chosen at request time) as fallback if the
|
|
80
|
+
// revealed item has no platform of its own.
|
|
81
|
+
platform.value = data?.platform || data?.platforms?.[0]?.name || platform.value
|
|
51
82
|
if (!keyCode.value) {
|
|
52
83
|
error.value = $i18n.t("keys.campaigns.error_reveal")
|
|
53
84
|
}
|
|
@@ -76,6 +107,13 @@ async function copyCode() {
|
|
|
76
107
|
}
|
|
77
108
|
}
|
|
78
109
|
|
|
110
|
+
function formatDate(dateString: string | null | undefined): string {
|
|
111
|
+
if (!dateString) return "-"
|
|
112
|
+
const date = new Date(dateString)
|
|
113
|
+
if (isNaN(date.getTime())) return "-"
|
|
114
|
+
return new Intl.DateTimeFormat(locale.value, { day: "numeric", month: "long", year: "numeric" }).format(date)
|
|
115
|
+
}
|
|
116
|
+
|
|
79
117
|
function goBack() {
|
|
80
118
|
if (typeof window !== "undefined" && window.history.length > 1) {
|
|
81
119
|
window.history.back()
|
|
@@ -103,49 +141,98 @@ function goBack() {
|
|
|
103
141
|
|
|
104
142
|
<div v-else class="content">
|
|
105
143
|
<div class="title-bar">
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<
|
|
110
|
-
</
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
<div v-if="!keyCode" class="reveal-block">
|
|
115
|
-
<p class="hint">{{ $t("keys.campaigns.reveal_hint") }}</p>
|
|
116
|
-
<button
|
|
117
|
-
class="btn"
|
|
118
|
-
:class="{ disabled: revealing }"
|
|
119
|
-
:disabled="revealing"
|
|
120
|
-
@click="reveal"
|
|
121
|
-
>
|
|
122
|
-
{{ revealing ? $t("keys.campaigns.revealing") : $t("keys.campaigns.status_key.reveal_key") }}
|
|
123
|
-
</button>
|
|
124
|
-
<div v-if="error" class="req-error">{{ error }}</div>
|
|
144
|
+
<h3>
|
|
145
|
+
<span class="accent-text">{{ $t("keys.campaigns.good") }}</span>
|
|
146
|
+
{{ $t("keys.campaigns.good_text") }}
|
|
147
|
+
<span class="accent-text">{{ currentUser?.nickname || currentUser?.name }}</span>
|
|
148
|
+
</h3>
|
|
149
|
+
<p class="description">{{ $t("keys.campaigns.approved_subtitle") }}</p>
|
|
150
|
+
<p v-if="requestData?.key?.name" class="campaign-name">{{ requestData.key.name }}</p>
|
|
151
|
+
<p v-if="platform" class="platform-name">{{ $t("keys.campaigns.platform") }}: {{ platform }}</p>
|
|
125
152
|
</div>
|
|
126
153
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
154
|
+
<div class="cards">
|
|
155
|
+
<div class="side">
|
|
156
|
+
<div class="code-list">
|
|
157
|
+
<div class="code">
|
|
158
|
+
<div class="number">1</div>
|
|
159
|
+
<div class="texts">
|
|
160
|
+
<div class="code-box" v-if="!keyCode">
|
|
161
|
+
<input type="text" value="••••••••••••••••" readonly class="code-masked" />
|
|
162
|
+
<button class="btn" :class="{ disabled: revealing }" :disabled="revealing" @click="reveal">
|
|
163
|
+
{{ revealing ? $t("keys.campaigns.revealing") : $t("keys.campaigns.status_key.reveal_key") }}
|
|
164
|
+
</button>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div class="code-box" v-else>
|
|
168
|
+
<input type="text" :value="keyCode" readonly class="code-value" />
|
|
169
|
+
<button class="btn" @click="copyCode">
|
|
170
|
+
<MGIcon :icon="copied ? 'check' : 'copy'" size="1x" />
|
|
171
|
+
{{ copied ? $t("keys.campaigns.copied") : $t("keys.campaigns.copy") }}
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div v-if="error" class="req-error">{{ error }}</div>
|
|
176
|
+
|
|
177
|
+
<div class="info">
|
|
178
|
+
<MGIcon size="1x" icon="version" />
|
|
179
|
+
{{ $t("keys.campaigns.cody_text") }}
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<div class="code">
|
|
185
|
+
<div class="number">2</div>
|
|
186
|
+
<div class="texts">
|
|
187
|
+
<div class="description">{{ $t("keys.campaigns.redeem_description_1") }}</div>
|
|
188
|
+
<a
|
|
189
|
+
v-if="keyCode && redeemUrl"
|
|
190
|
+
:href="redeemUrl"
|
|
191
|
+
target="_blank"
|
|
192
|
+
rel="noopener"
|
|
193
|
+
class="btn redeem-link"
|
|
194
|
+
>
|
|
195
|
+
{{ $t("keys.campaigns.redeem_at", { platform }) }}
|
|
196
|
+
</a>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div class="code">
|
|
201
|
+
<div class="number">3</div>
|
|
202
|
+
<div class="texts">
|
|
203
|
+
<div class="description">
|
|
204
|
+
{{ $t("keys.campaigns.redeem_description_2") }}
|
|
205
|
+
<br />
|
|
206
|
+
{{ $t("keys.campaigns.redeem_description_3") }}
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<div class="resources" v-if="requestData?.key?.press_kit_link || requestData?.key?.review_guideline || requestData?.key?.embargo_date">
|
|
213
|
+
<div class="resources-title">{{ $t("keys.campaigns.resources_title") }}</div>
|
|
214
|
+
|
|
215
|
+
<a
|
|
216
|
+
v-if="requestData?.key?.press_kit_link"
|
|
217
|
+
:href="requestData.key.press_kit_link"
|
|
218
|
+
target="_blank"
|
|
219
|
+
rel="noopener"
|
|
220
|
+
class="resource-link"
|
|
221
|
+
>
|
|
222
|
+
<MGIcon size="1x" icon="link" />
|
|
223
|
+
{{ $t("keys.campaigns.press_kit_link") }}
|
|
224
|
+
</a>
|
|
225
|
+
|
|
226
|
+
<div v-if="requestData?.key?.review_guideline" class="resource-item">
|
|
227
|
+
<div class="resource-label">{{ $t("keys.campaigns.review_guideline") }}</div>
|
|
228
|
+
<div class="resource-text">{{ requestData.key.review_guideline }}</div>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div v-if="requestData?.key?.embargo_date" class="resource-item">
|
|
232
|
+
<div class="resource-label">{{ $t("keys.campaigns.embargo_date") }}</div>
|
|
233
|
+
<div class="resource-text">{{ formatDate(requestData.key.embargo_date) }}</div>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
149
236
|
</div>
|
|
150
237
|
</div>
|
|
151
238
|
</div>
|
|
@@ -196,119 +283,153 @@ function goBack() {
|
|
|
196
283
|
& + & { margin-top: 8px; }
|
|
197
284
|
}
|
|
198
285
|
|
|
199
|
-
.content { display: flex; flex-direction: column; gap:
|
|
286
|
+
.content { display: flex; flex-direction: column; gap: 42px; }
|
|
200
287
|
|
|
201
288
|
.title-bar {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
289
|
+
h3 {
|
|
290
|
+
font-size: 24px;
|
|
291
|
+
font-weight: 700;
|
|
292
|
+
color: var(--card-article-title);
|
|
293
|
+
margin: 0 0 8px;
|
|
294
|
+
}
|
|
295
|
+
.accent-text { color: var(--key-accent, var(--primary, #D297FF)); }
|
|
296
|
+
.description { font-size: 14px; color: var(--secondary-info-fg); margin: 0; }
|
|
297
|
+
.campaign-name { font-size: 13px; font-weight: 600; color: var(--card-article-title); margin: 4px 0 0; }
|
|
298
|
+
.platform-name { font-size: 12px; color: var(--secondary-info-fg); margin: 2px 0 0; }
|
|
208
299
|
}
|
|
209
300
|
|
|
210
|
-
.
|
|
301
|
+
.cards {
|
|
211
302
|
display: flex;
|
|
212
|
-
|
|
213
|
-
gap: 16px;
|
|
214
|
-
padding: 24px;
|
|
215
|
-
background: var(--bg-app-badge);
|
|
216
|
-
border: 1px solid var(--button-secondary-default-bg);
|
|
217
|
-
|
|
218
|
-
.hint { font-size: 14px; color: var(--secondary-info-fg); margin: 0; }
|
|
303
|
+
width: 100%;
|
|
219
304
|
}
|
|
220
305
|
|
|
221
|
-
.
|
|
306
|
+
.side {
|
|
222
307
|
display: flex;
|
|
223
308
|
flex-direction: column;
|
|
224
|
-
gap:
|
|
225
|
-
padding: 24px;
|
|
226
|
-
background: var(--bg-app-badge);
|
|
227
|
-
border: 1px solid var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF))));
|
|
309
|
+
gap: 42px;
|
|
228
310
|
}
|
|
229
311
|
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
letter-spacing: 0.5px;
|
|
235
|
-
color: var(--secondary-info-fg);
|
|
312
|
+
.code-list {
|
|
313
|
+
display: flex;
|
|
314
|
+
flex-direction: column;
|
|
315
|
+
gap: 42px;
|
|
236
316
|
}
|
|
237
317
|
|
|
238
|
-
.
|
|
318
|
+
.code {
|
|
239
319
|
display: flex;
|
|
240
320
|
align-items: center;
|
|
241
|
-
gap:
|
|
242
|
-
|
|
243
|
-
|
|
321
|
+
gap: 24px;
|
|
322
|
+
|
|
323
|
+
.number {
|
|
324
|
+
display: flex;
|
|
325
|
+
width: 80px;
|
|
326
|
+
height: 80px;
|
|
327
|
+
padding: 24px;
|
|
328
|
+
flex-direction: column;
|
|
329
|
+
justify-content: center;
|
|
330
|
+
align-items: center;
|
|
331
|
+
background-color: var(--button-secondary-default-bg);
|
|
332
|
+
color: var(--key-accent, var(--primary, #D297FF));
|
|
333
|
+
text-align: center;
|
|
334
|
+
font-size: 28px;
|
|
335
|
+
font-weight: 700;
|
|
336
|
+
flex-shrink: 0;
|
|
337
|
+
}
|
|
244
338
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
color: var(--card-article-title);
|
|
251
|
-
word-break: break-all;
|
|
252
|
-
}
|
|
339
|
+
.texts {
|
|
340
|
+
display: flex;
|
|
341
|
+
flex-direction: column;
|
|
342
|
+
gap: 8px;
|
|
343
|
+
}
|
|
253
344
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
345
|
+
.code-box {
|
|
346
|
+
display: flex;
|
|
347
|
+
align-items: center;
|
|
348
|
+
gap: 6px;
|
|
349
|
+
width: 100%;
|
|
350
|
+
|
|
351
|
+
input {
|
|
352
|
+
display: flex;
|
|
353
|
+
padding: 12px;
|
|
354
|
+
background-color: transparent;
|
|
355
|
+
height: 48px;
|
|
356
|
+
border: 1px solid var(--bg-app-badge);
|
|
357
|
+
width: 100%;
|
|
358
|
+
font-size: 16px;
|
|
359
|
+
color: var(--secondary-info-fg);
|
|
360
|
+
&:focus { border-color: var(--key-accent, var(--primary, #D297FF)); outline: 0; }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
button {
|
|
364
|
+
background-color: var(--key-accent, var(--primary, #D297FF));
|
|
365
|
+
color: var(--key-accent-ink, #13161C);
|
|
366
|
+
height: 48px;
|
|
367
|
+
padding: 0 16px;
|
|
368
|
+
border: none;
|
|
369
|
+
cursor: pointer;
|
|
370
|
+
white-space: nowrap;
|
|
371
|
+
display: inline-flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
gap: 6px;
|
|
374
|
+
&.disabled, &:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
375
|
+
}
|
|
376
|
+
}
|
|
268
377
|
|
|
269
|
-
|
|
270
|
-
font-size: 12px;
|
|
271
|
-
color: var(--secondary-info-fg);
|
|
272
|
-
margin: 0;
|
|
273
|
-
}
|
|
378
|
+
.code-masked { letter-spacing: 3px; color: rgba(128, 128, 128, 0.5) !important; }
|
|
274
379
|
|
|
275
|
-
|
|
380
|
+
.info {
|
|
381
|
+
display: flex;
|
|
382
|
+
align-items: flex-end;
|
|
383
|
+
gap: 4px;
|
|
384
|
+
font-size: 12px;
|
|
385
|
+
color: var(--secondary-info-fg);
|
|
386
|
+
svg { color: var(--key-accent, var(--primary, #D297FF)); }
|
|
387
|
+
}
|
|
276
388
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
389
|
+
.description {
|
|
390
|
+
font-size: 12px;
|
|
391
|
+
color: var(--secondary-info-fg);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.redeem-link {
|
|
395
|
+
display: inline-flex;
|
|
396
|
+
width: fit-content;
|
|
397
|
+
margin-top: 8px;
|
|
398
|
+
padding: 8px 14px;
|
|
399
|
+
background: var(--key-accent, var(--primary, #D297FF));
|
|
400
|
+
color: var(--key-accent-ink, #13161C);
|
|
401
|
+
font-size: 13px;
|
|
402
|
+
text-decoration: none;
|
|
403
|
+
}
|
|
283
404
|
}
|
|
284
405
|
|
|
285
|
-
.
|
|
406
|
+
.req-error { font-size: 12px; color: var(--danger, #e53935); margin-top: 4px; }
|
|
407
|
+
|
|
408
|
+
.resources {
|
|
286
409
|
display: flex;
|
|
410
|
+
flex-direction: column;
|
|
287
411
|
gap: 12px;
|
|
288
|
-
|
|
412
|
+
padding: 20px;
|
|
413
|
+
background: var(--bg-app-badge);
|
|
414
|
+
border: 1px solid var(--button-secondary-default-bg);
|
|
289
415
|
}
|
|
290
416
|
|
|
291
|
-
.
|
|
292
|
-
color: var(--secondary-info-fg);
|
|
293
|
-
min-width: 100px;
|
|
294
|
-
}
|
|
417
|
+
.resources-title { font-size: 13px; font-weight: 600; color: var(--card-article-title); }
|
|
295
418
|
|
|
296
|
-
.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
color: var(--key-accent-ink, #13161C);
|
|
306
|
-
border: none;
|
|
307
|
-
cursor: pointer;
|
|
308
|
-
transition: opacity 0.15s;
|
|
309
|
-
&.disabled, &:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
310
|
-
&:not(.disabled):hover { opacity: 0.85; }
|
|
419
|
+
.resource-link {
|
|
420
|
+
display: inline-flex;
|
|
421
|
+
align-items: center;
|
|
422
|
+
gap: 6px;
|
|
423
|
+
width: fit-content;
|
|
424
|
+
font-size: 13px;
|
|
425
|
+
color: var(--key-accent, var(--primary, #D297FF));
|
|
426
|
+
text-decoration: none;
|
|
427
|
+
&:hover { text-decoration: underline; }
|
|
311
428
|
}
|
|
429
|
+
|
|
430
|
+
.resource-item { display: flex; flex-direction: column; gap: 2px; }
|
|
431
|
+
.resource-label { font-size: 12px; font-weight: 600; color: var(--card-article-title); }
|
|
432
|
+
.resource-text { font-size: 13px; color: var(--secondary-info-fg); }
|
|
312
433
|
}
|
|
313
434
|
|
|
314
435
|
@keyframes pulse {
|
|
@@ -71,9 +71,7 @@ onMounted(async () => {
|
|
|
71
71
|
}
|
|
72
72
|
})
|
|
73
73
|
|
|
74
|
-
const canSubmit = computed(() =>
|
|
75
|
-
!!selectedPlatformId.value && !!selectedRegionId.value && !!quantity.value && !submitting.value,
|
|
76
|
-
)
|
|
74
|
+
const canSubmit = computed(() => !submitting.value)
|
|
77
75
|
|
|
78
76
|
// Collapsible "Required data" box — matches tv's original toggle behavior
|
|
79
77
|
const isBoxOpen = ref(true)
|
|
@@ -104,16 +102,30 @@ const refusedReason = computed(() => campaign.value?.request_admin_notes || "")
|
|
|
104
102
|
|
|
105
103
|
async function submit() {
|
|
106
104
|
if (!canSubmit.value) return
|
|
107
|
-
submitting.value = true
|
|
108
105
|
requestError.value = ""
|
|
106
|
+
if (!selectedPlatformId.value || !selectedRegionId.value || !quantity.value || !description.value?.trim()) {
|
|
107
|
+
requestError.value = $i18n.t("keys.campaigns.required_fields")
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
submitting.value = true
|
|
109
111
|
try {
|
|
110
|
-
await requestKey({
|
|
112
|
+
const response = await requestKey({
|
|
111
113
|
key_id: keyId,
|
|
112
114
|
platform_ids: [selectedPlatformId.value],
|
|
113
115
|
region_ids: [selectedRegionId.value],
|
|
114
116
|
description: description.value || "-",
|
|
115
117
|
quantity_keys: Number(quantity.value) || 1,
|
|
116
118
|
})
|
|
119
|
+
const created = response?.data?.data ?? response?.data ?? {}
|
|
120
|
+
// requires_approval=false campaigns (or Pro/Business TV subscribers) get
|
|
121
|
+
// auto-approved by the backend observer synchronously — status 3 already
|
|
122
|
+
// reflects that by the time this response comes back, so skip the
|
|
123
|
+
// "request sent" wait screen and go straight to reveal.
|
|
124
|
+
const autoApproved = created.key_status_request_id === 3
|
|
125
|
+
if (autoApproved && created.id) {
|
|
126
|
+
navigateTo(`/${locale.value}/key-campaigns/redeem-key-approved?requestId=${created.id}`)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
117
129
|
success.value = true
|
|
118
130
|
setTimeout(() => {
|
|
119
131
|
navigateTo(`/${locale.value}/key-campaigns`)
|
|
@@ -233,11 +245,11 @@ function goBack() {
|
|
|
233
245
|
</div>
|
|
234
246
|
|
|
235
247
|
<div class="form-row">
|
|
236
|
-
<label class="label">{{ $t("keys.campaigns.
|
|
248
|
+
<label class="label">{{ $t("keys.campaigns.plan") }}</label>
|
|
237
249
|
<textarea
|
|
238
250
|
v-model="description"
|
|
239
251
|
class="textarea"
|
|
240
|
-
:placeholder="$t('keys.campaigns.
|
|
252
|
+
:placeholder="$t('keys.campaigns.plan_placeholder')"
|
|
241
253
|
rows="3"
|
|
242
254
|
/>
|
|
243
255
|
</div>
|
|
@@ -283,6 +283,9 @@ export const fetchCampaignBySlug = async (slug: string) => {
|
|
|
283
283
|
max_keys_for_user: k.max_keys_for_user ?? 1,
|
|
284
284
|
game_summary: k.game?.summary,
|
|
285
285
|
game_videos: (k.game?.videos || []) as Array<{ external_id: string; name?: string }>,
|
|
286
|
+
press_kit_link: k.press_kit_link ?? null,
|
|
287
|
+
review_guideline: k.review_guideline ?? null,
|
|
288
|
+
embargo_date: k.embargo_date ?? null,
|
|
286
289
|
request_id: k.requests?.[0]?.id,
|
|
287
290
|
request_item_id: k.requests?.[0]?.key_item_id,
|
|
288
291
|
request_admin_notes: k.requests?.[0]?.admin_notes ?? null,
|
package/services/gamesService.ts
CHANGED
|
@@ -44,5 +44,13 @@ export interface GameImage {
|
|
|
44
44
|
url_image_src: string
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export interface GameDetail {
|
|
48
|
+
platforms?: Array<{ id: number; name: string }>
|
|
49
|
+
genres?: Array<{ id: number; localized_name: string }>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const fetchDetailGame = (lang: string, slug: string) =>
|
|
53
|
+
client.get<{ data: GameDetail }>(`/${lang}/games/details/${slug}`)
|
|
54
|
+
|
|
47
55
|
export const fetchDetailGameImage = (lang: string, slug: string) =>
|
|
48
56
|
client.get<{ data: GameImage[] }>(`/${lang}/games/details/${slug}/images`)
|