@mundogamernetwork/shared-ui 1.1.80 → 1.1.82

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.
@@ -250,7 +250,7 @@ const detailRoute = computed(() =>
250
250
  top: 6px;
251
251
  right: 6px;
252
252
  background: var(--primary);
253
- color: #fff;
253
+ color: var(--key-accent-ink, #13161C);
254
254
  font-size: 11px;
255
255
  font-weight: 700;
256
256
  padding: 2px 7px;
@@ -280,7 +280,7 @@ const detailRoute = computed(() =>
280
280
  bottom: 6px;
281
281
  left: 6px;
282
282
  background: var(--primary);
283
- color: #fff;
283
+ color: var(--key-accent-ink, #13161C);
284
284
  font-size: 10px;
285
285
  font-weight: 700;
286
286
  padding: 3px 7px;
package/locales/de.json CHANGED
@@ -174,7 +174,17 @@
174
174
  "error_duplicate": "Sie haben bereits eine Anfrage für diese Kampagne. Überprüfen Sie 'Meine Bestellungen' für den Status.",
175
175
  "error_user_blocked": "Ihr Zugang zu Key-Anfragen wurde vorübergehend gesperrt, da Material-Einreichungen überfällig sind. Bitte reichen Sie Ihre ausstehenden Materialien ein.",
176
176
  "error_not_eligible": "Diese Kampagne ist für Ihren aktuellen Plan nicht verfügbar.",
177
- "error_pending_limit": "Sie haben das gleichzeitige Anfragenlimit Ihres Plans erreicht. Bitte erfüllen Sie Ihre ausstehenden Verpflichtungen, bevor Sie einen neuen Key anfordern."
177
+ "error_pending_limit": "Sie haben das gleichzeitige Anfragenlimit Ihres Plans erreicht. Bitte erfüllen Sie Ihre ausstehenden Verpflichtungen, bevor Sie einen neuen Key anfordern.",
178
+ "select_option": "Option auswählen",
179
+ "required_data": "Erforderliche Angaben",
180
+ "already_requested": "Sie haben bereits eine Anfrage für diese Kampagne gestellt.",
181
+ "back_to_campaign": "Zur Kampagne zurückkehren",
182
+ "status_pending": "Anfrage ausstehend — bitte warten Sie auf die Genehmigung",
183
+ "status_approved": "Anfrage genehmigt — Sie können jetzt Ihren Schlüssel abrufen",
184
+ "status_completed": "Anfrage abgeschlossen",
185
+ "status_refused": "Anfrage abgelehnt",
186
+ "status_cancelled": "Anfrage storniert",
187
+ "refused_reason": "Grund der Ablehnung:"
178
188
  },
179
189
  "materials": {
180
190
  "title": "Submit material",
package/locales/en.json CHANGED
@@ -174,7 +174,17 @@
174
174
  "error_duplicate": "You already have a request for this campaign. Check 'My Orders' to see the status.",
175
175
  "error_user_blocked": "Your key request access has been temporarily blocked due to overdue material submissions. Please submit your pending materials to restore access.",
176
176
  "error_not_eligible": "This campaign is not available for your current plan.",
177
- "error_pending_limit": "You have reached the simultaneous request limit for your plan. Please fulfill your pending obligations before requesting a new key."
177
+ "error_pending_limit": "You have reached the simultaneous request limit for your plan. Please fulfill your pending obligations before requesting a new key.",
178
+ "select_option": "Select an option",
179
+ "required_data": "Required data",
180
+ "already_requested": "You have already submitted a request for this campaign.",
181
+ "back_to_campaign": "Back to campaign",
182
+ "status_pending": "Request pending — waiting for approval",
183
+ "status_approved": "Request approved — you can now retrieve your key",
184
+ "status_completed": "Request completed",
185
+ "status_refused": "Request refused",
186
+ "status_cancelled": "Request cancelled",
187
+ "refused_reason": "Reason for refusal:"
178
188
  },
179
189
  "materials": {
180
190
  "title": "Submit material",
@@ -174,7 +174,17 @@
174
174
  "error_duplicate": "Você já tem um pedido para esta campanha. Acesse 'Meus Pedidos' para ver o status.",
175
175
  "error_user_blocked": "Seu acesso a pedidos de chave está temporariamente bloqueado por materiais não entregues. Envie seus materiais pendentes para restaurar o acesso.",
176
176
  "error_not_eligible": "Esta campanha não está disponível para o seu plano atual.",
177
- "error_pending_limit": "Você atingiu o limite de pedidos simultâneos do seu plano. Entregue o material pendente antes de solicitar uma nova chave."
177
+ "error_pending_limit": "Você atingiu o limite de pedidos simultâneos do seu plano. Entregue o material pendente antes de solicitar uma nova chave.",
178
+ "select_option": "Selecione uma opção",
179
+ "required_data": "Dados obrigatórios",
180
+ "already_requested": "Você já enviou uma solicitação para esta campanha.",
181
+ "back_to_campaign": "Voltar para a campanha",
182
+ "status_pending": "Solicitação pendente — aguardando aprovação",
183
+ "status_approved": "Solicitação aprovada — você pode resgatar sua chave",
184
+ "status_completed": "Solicitação concluída",
185
+ "status_refused": "Solicitação recusada",
186
+ "status_cancelled": "Solicitação cancelada",
187
+ "refused_reason": "Motivo da recusa:"
178
188
  },
179
189
  "materials": {
180
190
  "title": "Submit material",
package/locales/ro.json CHANGED
@@ -174,7 +174,17 @@
174
174
  "error_duplicate": "Aveți deja o cerere pentru această campanie. Verificați 'Comenzile mele' pentru status.",
175
175
  "error_user_blocked": "Accesul dvs. la solicitările de chei a fost blocat temporar din cauza transmisiilor de materiale restante. Trimiteți materialele dvs. pentru a restabili accesul.",
176
176
  "error_not_eligible": "Această campanie nu este disponibilă pentru planul dvs. actual.",
177
- "error_pending_limit": "Ați atins limita de cereri simultane a planului dvs. Îndepliniți-vă obligațiile restante înainte de a solicita o nouă cheie."
177
+ "error_pending_limit": "Ați atins limita de cereri simultane a planului dvs. Îndepliniți-vă obligațiile restante înainte de a solicita o nouă cheie.",
178
+ "select_option": "Selectează o opțiune",
179
+ "required_data": "Date obligatorii",
180
+ "already_requested": "Ați trimis deja o cerere pentru această campanie.",
181
+ "back_to_campaign": "Înapoi la campanie",
182
+ "status_pending": "Cerere în așteptare — așteptați aprobarea",
183
+ "status_approved": "Cerere aprobată — puteți prelua cheia",
184
+ "status_completed": "Cerere finalizată",
185
+ "status_refused": "Cerere refuzată",
186
+ "status_cancelled": "Cerere anulată",
187
+ "refused_reason": "Motivul refuzului:"
178
188
  },
179
189
  "materials": {
180
190
  "title": "Submit material",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mundogamernetwork/shared-ui",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "description": "Mundo Gamer Network - Shared UI Layer (Nuxt 3)",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -302,7 +302,7 @@ function goBack() {
302
302
  font-size: 14px;
303
303
  font-weight: 700;
304
304
  background: var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF))));
305
- color: #fff;
305
+ color: var(--key-accent-ink, #13161C);
306
306
  border: none;
307
307
  cursor: pointer;
308
308
  transition: opacity 0.15s;
@@ -75,6 +75,33 @@ const canSubmit = computed(() =>
75
75
  !!selectedPlatformId.value && !!selectedRegionId.value && !!quantity.value && !submitting.value,
76
76
  )
77
77
 
78
+ // Collapsible "Required data" box — matches tv's original toggle behavior
79
+ const isBoxOpen = ref(true)
80
+ function toggleBox() {
81
+ isBoxOpen.value = !isBoxOpen.value
82
+ }
83
+
84
+ // request_status: null/undefined/1 = no request yet, anything else = existing request
85
+ const hasExistingRequest = computed(() => {
86
+ const s = campaign.value?.request_status
87
+ return s !== null && s !== undefined && s !== 1
88
+ })
89
+
90
+ const existingRequestStatus = computed(() => {
91
+ switch (campaign.value?.request_status) {
92
+ case 2: return $i18n.t("keys.campaigns.status_pending")
93
+ case 3: return $i18n.t("keys.campaigns.status_approved")
94
+ case 4: return $i18n.t("keys.campaigns.status_approved")
95
+ case 5: return $i18n.t("keys.campaigns.status_completed")
96
+ case 6: return $i18n.t("keys.campaigns.status_refused")
97
+ case 7: return $i18n.t("keys.campaigns.status_cancelled")
98
+ default: return ""
99
+ }
100
+ })
101
+
102
+ const isRefused = computed(() => campaign.value?.request_status === 6)
103
+ const refusedReason = computed(() => campaign.value?.request_admin_notes || "")
104
+
78
105
  async function submit() {
79
106
  if (!canSubmit.value) return
80
107
  submitting.value = true
@@ -147,63 +174,74 @@ function goBack() {
147
174
  <p v-if="campaign?.name" class="campaign-name">{{ campaign.name }}</p>
148
175
  </div>
149
176
 
150
- <form class="form" @submit.prevent="submit">
151
- <!-- Platform -->
152
- <div class="field">
153
- <label class="label">{{ $t("keys.campaigns.platform") }}</label>
154
- <div class="options">
155
- <button
156
- v-for="p in platforms"
157
- :key="p.id"
158
- type="button"
159
- class="option"
160
- :class="{ selected: selectedPlatformId === p.id }"
161
- @click="selectedPlatformId = p.id"
162
- >
163
- {{ p.name }}
164
- </button>
177
+ <!-- Already has a request -->
178
+ <div v-if="hasExistingRequest" class="existing-request-notice">
179
+ <MGIcon size="1x" icon="version" class="notice-icon" />
180
+ <div class="notice-texts">
181
+ <div class="notice-title">{{ $t("keys.campaigns.already_requested") }}</div>
182
+ <div class="notice-status">{{ existingRequestStatus }}</div>
183
+ <div v-if="isRefused && refusedReason" class="notice-reason">
184
+ <span class="notice-reason-label">{{ $t("keys.campaigns.refused_reason") }}</span>
185
+ <span class="notice-reason-text">{{ refusedReason }}</span>
165
186
  </div>
166
187
  </div>
188
+ <button class="btn notice-back" @click="goBack">
189
+ {{ $t("keys.campaigns.back_to_campaign") }}
190
+ </button>
191
+ </div>
167
192
 
168
- <!-- Region -->
169
- <div class="field">
170
- <label class="label">{{ $t("keys.campaigns.region") }}</label>
171
- <div class="options">
172
- <button
173
- v-for="r in regions"
174
- :key="r.id"
175
- type="button"
176
- class="option"
177
- :class="{ selected: selectedRegionId === r.id }"
178
- @click="selectedRegionId = r.id"
179
- >
180
- {{ r.name }}
193
+ <form v-else class="form" @submit.prevent="submit">
194
+ <div class="required-data">
195
+ <div class="required-data-header" @click="toggleBox">
196
+ <div class="required-data-header-left">
197
+ <MGIcon size="1x" icon="check" :style="{ color: isBoxOpen ? 'var(--key-accent, var(--primary, #D297FF))' : 'var(--inactive)' }" />
198
+ <span>{{ $t("keys.campaigns.required_data") }}</span>
199
+ </div>
200
+ <button type="button" class="box-toggler" :aria-expanded="isBoxOpen">
201
+ <MGIcon icon="chevron-down" :class="{ rotate: isBoxOpen }" />
181
202
  </button>
182
203
  </div>
183
- </div>
184
204
 
185
- <!-- Quantity -->
186
- <div class="field" v-if="maxKeysForUser > 1">
187
- <label class="label">{{ $t("keys.campaigns.quantity") }}</label>
188
- <input
189
- v-model.number="quantity"
190
- type="number"
191
- min="1"
192
- :max="maxKeysForUser"
193
- class="textarea quantity-input"
194
- @change="clampQuantity"
195
- />
196
- </div>
197
-
198
- <!-- Description (optional) -->
199
- <div class="field">
200
- <label class="label">{{ $t("keys.campaigns.description_optional") }}</label>
201
- <textarea
202
- v-model="description"
203
- class="textarea"
204
- :placeholder="$t('keys.campaigns.description_placeholder')"
205
- rows="3"
206
- />
205
+ <div v-show="isBoxOpen" class="required-data-body">
206
+ <div class="form-row">
207
+ <label class="label">{{ $t("keys.campaigns.platform") }}</label>
208
+ <select v-model.number="selectedPlatformId" class="select">
209
+ <option :value="null" disabled>{{ $t("keys.campaigns.select_option") }}</option>
210
+ <option v-for="p in platforms" :key="p.id" :value="p.id">{{ p.name }}</option>
211
+ </select>
212
+ </div>
213
+
214
+ <div class="form-row">
215
+ <label class="label">{{ $t("keys.campaigns.region") }}</label>
216
+ <select v-model.number="selectedRegionId" class="select">
217
+ <option :value="null" disabled>{{ $t("keys.campaigns.select_option") }}</option>
218
+ <option v-for="r in regions" :key="r.id" :value="r.id">{{ r.name || r.localized_name }}</option>
219
+ </select>
220
+ </div>
221
+
222
+ <div class="form-row" v-if="maxKeysForUser > 1">
223
+ <label class="label">{{ $t("keys.campaigns.quantity") }}</label>
224
+ <input
225
+ v-model.number="quantity"
226
+ type="number"
227
+ min="1"
228
+ :max="maxKeysForUser"
229
+ class="input quantity-input"
230
+ :placeholder="$t('keys.campaigns.quantity')"
231
+ @change="clampQuantity"
232
+ />
233
+ </div>
234
+
235
+ <div class="form-row">
236
+ <label class="label">{{ $t("keys.campaigns.description_optional") }}</label>
237
+ <textarea
238
+ v-model="description"
239
+ class="textarea"
240
+ :placeholder="$t('keys.campaigns.description_placeholder')"
241
+ rows="3"
242
+ />
243
+ </div>
244
+ </div>
207
245
  </div>
208
246
 
209
247
  <div v-if="requestError" class="req-error">{{ requestError }}</div>
@@ -276,59 +314,120 @@ function goBack() {
276
314
 
277
315
  .form { display: flex; flex-direction: column; gap: 24px; }
278
316
 
279
- .field {
317
+ .required-data {
280
318
  display: flex;
281
319
  flex-direction: column;
282
- gap: 10px;
320
+ background: var(--bg-app-badge);
321
+ border: 1px solid var(--button-secondary-default-bg);
283
322
  }
284
323
 
285
- .label {
286
- font-size: 13px;
324
+ .required-data-header {
325
+ display: flex;
326
+ align-items: center;
327
+ justify-content: space-between;
328
+ gap: 10px;
329
+ padding: 16px 20px;
330
+ font-size: 14px;
287
331
  font-weight: 600;
288
332
  color: var(--card-article-title);
333
+ border-bottom: 1px solid var(--button-secondary-default-bg);
334
+ cursor: pointer;
289
335
  }
290
336
 
291
- .options {
337
+ .required-data-header-left {
292
338
  display: flex;
293
- flex-wrap: wrap;
294
- gap: 8px;
339
+ align-items: center;
340
+ gap: 10px;
295
341
  }
296
342
 
297
- .option {
298
- padding: 8px 16px;
299
- font-size: 13px;
300
- font-weight: 500;
301
- border: 1px solid var(--button-secondary-default-bg);
343
+ .box-toggler {
344
+ display: flex;
345
+ align-items: center;
302
346
  background: transparent;
347
+ border: none;
303
348
  color: var(--secondary-info-fg);
304
349
  cursor: pointer;
305
- transition: border-color 0.15s, color 0.15s;
306
350
 
307
- &:hover { border-color: var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF)))); color: var(--card-article-title); }
308
- &.selected {
309
- border-color: var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF))));
310
- color: var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF))));
311
- background: var(--key-accent-subtle-bg, rgba(210, 151, 255, 0.08));
312
- }
351
+ svg { transition: transform 0.15s; }
352
+ svg.rotate { transform: rotate(180deg); }
313
353
  }
314
354
 
315
- .quantity-input {
316
- width: 120px;
355
+ .existing-request-notice {
356
+ display: flex;
357
+ align-items: flex-start;
358
+ gap: 14px;
359
+ padding: 20px;
360
+ background: var(--bg-app-badge);
361
+ border: 1px solid var(--button-secondary-default-bg);
317
362
  }
318
363
 
319
- .textarea {
320
- width: 100%;
321
- background: var(--bg-app-badge);
364
+ .notice-icon { color: var(--key-accent, var(--primary, #D297FF)); margin-top: 2px; }
365
+
366
+ .notice-texts { flex: 1; display: flex; flex-direction: column; gap: 4px; }
367
+
368
+ .notice-title { font-size: 14px; font-weight: 600; color: var(--card-article-title); }
369
+ .notice-status { font-size: 13px; color: var(--secondary-info-fg); }
370
+
371
+ .notice-reason {
372
+ display: flex;
373
+ flex-direction: column;
374
+ gap: 2px;
375
+ margin-top: 6px;
376
+ font-size: 13px;
377
+
378
+ .notice-reason-label { font-weight: 600; color: var(--card-article-title); }
379
+ .notice-reason-text { color: var(--secondary-info-fg); }
380
+ }
381
+
382
+ .notice-back {
383
+ background: transparent;
322
384
  border: 1px solid var(--button-secondary-default-bg);
323
385
  color: var(--card-article-title);
386
+ padding: 8px 16px;
387
+ font-size: 13px;
388
+ font-weight: 600;
389
+ cursor: pointer;
390
+ white-space: nowrap;
391
+ &:hover { border-color: var(--key-accent, var(--primary, #D297FF)); }
392
+ }
393
+
394
+ .form-row {
395
+ display: flex;
396
+ flex-direction: column;
397
+ gap: 10px;
398
+ padding: 20px;
399
+ & + .form-row { border-top: 1px solid var(--button-secondary-default-bg); }
400
+ }
401
+
402
+ .label {
403
+ font-size: 13px;
404
+ font-weight: 600;
405
+ color: var(--card-article-title);
406
+ }
407
+
408
+ .select, .input, .textarea {
409
+ width: 100%;
410
+ background-color: transparent;
411
+ border: 1px solid rgba(128, 128, 128, 0.46);
412
+ color: var(--secondary-info-fg);
324
413
  font-size: 13px;
325
414
  padding: 10px 12px;
326
- resize: vertical;
327
415
  outline: none;
328
416
  font-family: inherit;
329
417
  &:focus { border-color: var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF)))); }
330
418
  }
331
419
 
420
+ .select {
421
+ appearance: auto;
422
+ cursor: pointer;
423
+ }
424
+
425
+ .textarea { resize: vertical; }
426
+
427
+ .quantity-input {
428
+ width: 120px;
429
+ }
430
+
332
431
  .req-error {
333
432
  font-size: 13px;
334
433
  color: var(--danger, #e53935);
@@ -341,7 +440,7 @@ function goBack() {
341
440
  font-size: 14px;
342
441
  font-weight: 700;
343
442
  background: var(--key-accent, var(--primary, var(--key-accent, var(--primary, #D297FF))));
344
- color: #fff;
443
+ color: var(--key-accent-ink, #13161C);
345
444
  border: none;
346
445
  cursor: pointer;
347
446
  transition: opacity 0.15s;