@mundogamernetwork/shared-ui 1.1.92 → 1.1.94

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.
@@ -756,6 +756,13 @@ body:has(.kit-press) {
756
756
  scrollbar-width: none;
757
757
  }
758
758
  .kit-anchornav-inner::-webkit-scrollbar { display: none; }
759
+ /* Trailing spacer so the last item isn't clipped by the overflow container */
760
+ .kit-anchornav-inner::after {
761
+ content: '';
762
+ display: block;
763
+ min-width: 24px;
764
+ flex-shrink: 0;
765
+ }
759
766
  .kit-anchornav-item {
760
767
  display: inline-block;
761
768
  padding: 14px 16px;
package/locales/de.json CHANGED
@@ -218,6 +218,9 @@
218
218
  "type_article": "Article / Review",
219
219
  "type_web": "Website / Blog",
220
220
  "type_print": "Screenshot / Print",
221
+ "deadline_days_left": "Noch {days} Tage, um dein Material einzureichen",
222
+ "deadline_today": "Letzter Tag, um dein Material einzureichen",
223
+ "deadline_overdue": "Die Einreichungsfrist ist abgelaufen",
221
224
  "required_fields": "Fill in all required fields",
222
225
  "url_error": "Invalid URL",
223
226
  "submit_error": "Error submitting. Try again.",
package/locales/en.json CHANGED
@@ -218,6 +218,9 @@
218
218
  "type_article": "Article / Review",
219
219
  "type_web": "Website / Blog",
220
220
  "type_print": "Screenshot / Print",
221
+ "deadline_days_left": "{days} days left to submit your material",
222
+ "deadline_today": "Last day to submit your material",
223
+ "deadline_overdue": "The submission deadline has passed",
221
224
  "required_fields": "Fill in all required fields",
222
225
  "url_error": "Invalid URL",
223
226
  "submit_error": "Error submitting. Try again.",
@@ -218,6 +218,9 @@
218
218
  "type_article": "Article / Review",
219
219
  "type_web": "Website / Blog",
220
220
  "type_print": "Screenshot / Print",
221
+ "deadline_days_left": "Faltam {days} dias para enviar seu material",
222
+ "deadline_today": "Último dia para enviar seu material",
223
+ "deadline_overdue": "O prazo de envio já passou",
221
224
  "required_fields": "Fill in all required fields",
222
225
  "url_error": "Invalid URL",
223
226
  "submit_error": "Error submitting. Try again.",
package/locales/ro.json CHANGED
@@ -218,6 +218,9 @@
218
218
  "type_article": "Article / Review",
219
219
  "type_web": "Website / Blog",
220
220
  "type_print": "Screenshot / Print",
221
+ "deadline_days_left": "Mai ai {days} zile pentru a trimite materialul",
222
+ "deadline_today": "Ultima zi pentru a trimite materialul",
223
+ "deadline_overdue": "Termenul de trimitere a expirat",
221
224
  "required_fields": "Fill in all required fields",
222
225
  "url_error": "Invalid URL",
223
226
  "submit_error": "Error submitting. Try again.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mundogamernetwork/shared-ui",
3
- "version": "1.1.92",
3
+ "version": "1.1.94",
4
4
  "description": "Mundo Gamer Network - Shared UI Layer (Nuxt 3)",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { ref, computed, inject, onMounted } from "vue"
2
+ import { ref, computed, inject, onMounted, unref } from "vue"
3
3
  import { useNuxtApp, useRoute, useAsyncData, navigateTo } from "#app"
4
4
  import {
5
5
  fetchCampaignBySlug,
@@ -476,7 +476,7 @@ const tierLabel = computed(() => {
476
476
  // Auth guard for CTA actions
477
477
  // ------------------------------------------------------------------
478
478
  function requireAuth(targetPath: string) {
479
- if (currentUser) {
479
+ if (unref(currentUser)) {
480
480
  navigateTo(targetPath)
481
481
  return
482
482
  }
@@ -497,7 +497,7 @@ const showUsersModal = ref(false)
497
497
  onMounted(async () => {
498
498
  updateCountdown()
499
499
 
500
- if (currentUser && campaign.value?.id) {
500
+ if (unref(currentUser) && campaign.value?.id) {
501
501
  try {
502
502
  const response = await fetchMyRequests({ "filter[key_id]": campaign.value.id })
503
503
  const requests = response?.data?.data
@@ -15,6 +15,11 @@
15
15
  </div>
16
16
  </div>
17
17
 
18
+ <div v-if="deadlineText" class="deadline-banner" :class="{ overdue: daysRemaining !== null && daysRemaining < 0 }">
19
+ <MGIcon size="1x" icon="others" />
20
+ <span>{{ deadlineText }}</span>
21
+ </div>
22
+
18
23
  <div class="boxes">
19
24
  <!-- Submission form -->
20
25
  <div class="box-container">
@@ -187,10 +192,10 @@
187
192
  </template>
188
193
 
189
194
  <script setup lang="ts">
190
- import { ref, inject, onMounted } from "vue"
195
+ import { ref, computed, inject, onMounted, unref } from "vue"
191
196
  import { useRoute, useRouter } from "vue-router"
192
197
  import { useI18n } from "#imports"
193
- import { submitMaterial, fetchMaterialsByRequest } from "../../services/campaignService"
198
+ import { submitMaterial, fetchMaterialsByRequest, fetchKeyRequestById } from "../../services/campaignService"
194
199
 
195
200
  definePageMeta({
196
201
  middleware: [
@@ -241,6 +246,30 @@ const urlError = ref<string | null>(null)
241
246
  const formError = ref<string | null>(null)
242
247
  const showSuccessModal = ref(false)
243
248
 
249
+ // Submission deadline — counts down from the request's approval date, using
250
+ // the campaign's configured window (defaults to 30 days server-side).
251
+ const daysRemaining = ref<number | null>(null)
252
+ const deadlineText = computed(() => {
253
+ if (daysRemaining.value === null) return ""
254
+ if (daysRemaining.value < 0) return t("keys.materials.deadline_overdue")
255
+ if (daysRemaining.value === 0) return t("keys.materials.deadline_today")
256
+ return t("keys.materials.deadline_days_left", { days: daysRemaining.value })
257
+ })
258
+
259
+ async function loadDeadline() {
260
+ try {
261
+ const res = await fetchKeyRequestById(requestId.value)
262
+ const data = res?.data?.data ?? res?.data
263
+ const approvedAt = data?.approved_at
264
+ const deadlineDays = Number(data?.material_deadline_days ?? 30)
265
+ if (!approvedAt) return
266
+ const deadline = new Date(approvedAt)
267
+ deadline.setDate(deadline.getDate() + deadlineDays)
268
+ const diffMs = deadline.getTime() - Date.now()
269
+ daysRemaining.value = Math.ceil(diffMs / (1000 * 60 * 60 * 24))
270
+ } catch { /* deadline is informational only, ignore failures */ }
271
+ }
272
+
244
273
  function toggleType(formIndex: number, type: ContentType) {
245
274
  const currentTypes = forms.value[formIndex].content_types
246
275
  const idx = currentTypes.indexOf(type)
@@ -312,7 +341,7 @@ const submitForm = async () => {
312
341
  for (const form of forms.value) {
313
342
  await submitMaterial({
314
343
  key_request_id: requestId.value,
315
- user_id: currentUserId,
344
+ user_id: unref(currentUserId),
316
345
  content_types: form.content_types.map((type) => contentTypesMap[type]),
317
346
  material_url: form.material_url,
318
347
  description: form.description,
@@ -343,6 +372,7 @@ onMounted(async () => {
343
372
  const res = await fetchMaterialsByRequest(requestId.value)
344
373
  submittedMaterials.value = res?.data?.data ?? []
345
374
  } catch { /* silent */ }
375
+ loadDeadline()
346
376
  }
347
377
  })
348
378
  </script>
@@ -434,6 +464,24 @@ onMounted(async () => {
434
464
  }
435
465
  }
436
466
 
467
+ .deadline-banner {
468
+ display: flex;
469
+ align-items: center;
470
+ gap: 8px;
471
+ padding: 10px 16px;
472
+ background: rgba(210, 151, 255, 0.1);
473
+ border-left: 2px solid #d297ff;
474
+ color: var(--card-cover-title);
475
+ font-size: 13px;
476
+ i { color: #d297ff; }
477
+
478
+ &.overdue {
479
+ background: rgba(248, 60, 60, 0.1);
480
+ border-left-color: rgb(248, 60, 60);
481
+ i { color: rgb(248, 60, 60); }
482
+ }
483
+ }
484
+
437
485
  .disabled {
438
486
  cursor: not-allowed;
439
487
  pointer-events: none;
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { ref, inject, onMounted } from "vue"
2
+ import { ref, inject, onMounted, unref } from "vue"
3
3
  import { useRoute, navigateTo } from "#app"
4
4
  import { useNuxtApp } from "#app"
5
5
  import { revealKey, fetchKeyRequestById } from "../../services/campaignService"
@@ -47,7 +47,7 @@ const redeemUrl = computed(() => {
47
47
  })
48
48
 
49
49
  onMounted(async () => {
50
- if (!currentUser) {
50
+ if (!unref(currentUser)) {
51
51
  const returnTo = typeof window !== "undefined" ? window.location.href : ""
52
52
  navigateTo(`${loginUrl}?redirect_to=${encodeURIComponent(returnTo)}`, { external: true })
53
53
  return
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { ref, computed, inject, onMounted } from "vue"
2
+ import { ref, computed, inject, onMounted, unref } from "vue"
3
3
  import { useRoute, navigateTo } from "#app"
4
4
  import { useNuxtApp } from "#app"
5
5
  import {
@@ -46,7 +46,7 @@ function clampQuantity() {
46
46
  }
47
47
 
48
48
  onMounted(async () => {
49
- if (!currentUser) {
49
+ if (!unref(currentUser)) {
50
50
  const returnTo = typeof window !== "undefined" ? window.location.href : ""
51
51
  navigateTo(`${loginUrl}?redirect_to=${encodeURIComponent(returnTo)}`, { external: true })
52
52
  return
@@ -96,6 +96,15 @@ function scrollToTop() {
96
96
  window.scrollTo({ top: 0, behavior: 'smooth' });
97
97
  }
98
98
 
99
+ function scrollToSection(id: string) {
100
+ const el = document.getElementById(id);
101
+ if (!el) return;
102
+ const nav = document.querySelector('.kit-anchornav') as HTMLElement | null;
103
+ const offset = nav ? nav.offsetHeight : 52;
104
+ const top = el.getBoundingClientRect().top + window.scrollY - offset;
105
+ window.scrollTo({ top, behavior: 'smooth' });
106
+ }
107
+
99
108
  onMounted(() => {
100
109
  const hero = document.querySelector('.kit-cover') as HTMLElement | null;
101
110
  heroHeight = hero ? hero.offsetHeight : 300;
@@ -119,7 +128,7 @@ const navSections = computed(() => {
119
128
  if (coverage.value.length) s.push({ id: 'creators', label: 'kit.press.coverage' });
120
129
  if (awards.value.length) s.push({ id: 'awards', label: 'kit.press.awards' });
121
130
  if (team.value.length) s.push({ id: 'team', label: 'kit.press.team' });
122
- if (showcases.value.length) s.push({ id: 'events', label: 'kit.press.showcases' });
131
+ if (hasEcosystem.value) s.push({ id: 'events', label: 'kit.press.showcases' });
123
132
  if (k.press_contact_email) s.push({ id: 'contact', label: 'kit.press.contact' });
124
133
  return s;
125
134
  });
@@ -221,6 +230,7 @@ useSeoMeta({
221
230
  :key="s.id"
222
231
  :href="`#${s.id}`"
223
232
  class="kit-anchornav-item"
233
+ @click.prevent="scrollToSection(s.id)"
224
234
  >{{ $te(s.label) ? $t(s.label) : s.label }}</a>
225
235
  </div>
226
236
  </nav>