@mundogamernetwork/shared-ui 1.5.0 → 1.6.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.
- package/components/MediaKit/MgMediaKitLinks.vue +121 -0
- package/components/MediaKit/MgMediaKitStore.vue +107 -0
- package/components/form/SortInput.vue +9 -2
- package/nuxt.config.ts +10 -3
- package/package.json +1 -1
- package/pages/key-campaigns/index.vue +7 -6
- package/pages/media-kit/[slug].vue +180 -39
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
type MediaKitLink = { label: string; url: string };
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(defineProps<{ links?: MediaKitLink[] }>(), {
|
|
5
|
+
links: () => [],
|
|
6
|
+
});
|
|
7
|
+
const emit = defineEmits<{ update: [key: 'custom_links', value: MediaKitLink[]] }>();
|
|
8
|
+
|
|
9
|
+
const draft = ref<MediaKitLink[]>([]);
|
|
10
|
+
const saved = ref(false);
|
|
11
|
+
|
|
12
|
+
watch(
|
|
13
|
+
() => props.links,
|
|
14
|
+
(value) => {
|
|
15
|
+
draft.value = (value || []).map(link => ({ label: link.label || '', url: link.url || '' }));
|
|
16
|
+
},
|
|
17
|
+
{ immediate: true, deep: true },
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const addLink = () => {
|
|
21
|
+
if (draft.value.length < 12) draft.value.push({ label: '', url: '' });
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const removeLink = (index: number) => draft.value.splice(index, 1);
|
|
25
|
+
|
|
26
|
+
const move = (index: number, direction: -1 | 1) => {
|
|
27
|
+
const target = index + direction;
|
|
28
|
+
if (target < 0 || target >= draft.value.length) return;
|
|
29
|
+
const [link] = draft.value.splice(index, 1);
|
|
30
|
+
draft.value.splice(target, 0, link);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const canSave = computed(() => draft.value.every(link => {
|
|
34
|
+
if (!link.label.trim() || !link.url.trim()) return false;
|
|
35
|
+
try {
|
|
36
|
+
const parsed = new URL(link.url);
|
|
37
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
const save = () => {
|
|
44
|
+
if (!canSave.value) return;
|
|
45
|
+
emit('update', 'custom_links', draft.value.map(link => ({
|
|
46
|
+
label: link.label.trim(),
|
|
47
|
+
url: link.url.trim(),
|
|
48
|
+
})));
|
|
49
|
+
saved.value = true;
|
|
50
|
+
setTimeout(() => { saved.value = false; }, 1800);
|
|
51
|
+
};
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<template>
|
|
55
|
+
<div class="mk-links-editor">
|
|
56
|
+
<div class="mk-links-head">
|
|
57
|
+
<div>
|
|
58
|
+
<h3>{{ $t('media_kit.links.title') }}</h3>
|
|
59
|
+
<p>{{ $t('media_kit.links.description') }}</p>
|
|
60
|
+
</div>
|
|
61
|
+
<button type="button" class="mk-link-add" :disabled="draft.length >= 12" @click="addLink">
|
|
62
|
+
+ {{ $t('media_kit.links.add') }}
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div v-if="draft.length" class="mk-link-list">
|
|
67
|
+
<div v-for="(link, index) in draft" :key="index" class="mk-link-row">
|
|
68
|
+
<span class="mk-link-number">{{ index + 1 }}</span>
|
|
69
|
+
<label>
|
|
70
|
+
<span>{{ $t('media_kit.links.label') }}</span>
|
|
71
|
+
<input v-model="link.label" type="text" maxlength="80" :placeholder="$t('media_kit.links.label_placeholder')" />
|
|
72
|
+
</label>
|
|
73
|
+
<label class="mk-link-url">
|
|
74
|
+
<span>{{ $t('media_kit.links.url') }}</span>
|
|
75
|
+
<input v-model="link.url" type="url" maxlength="2048" placeholder="https://" />
|
|
76
|
+
</label>
|
|
77
|
+
<div class="mk-link-actions">
|
|
78
|
+
<button type="button" :disabled="index === 0" :aria-label="$t('media_kit.links.move_up')" @click="move(index, -1)">↑</button>
|
|
79
|
+
<button type="button" :disabled="index === draft.length - 1" :aria-label="$t('media_kit.links.move_down')" @click="move(index, 1)">↓</button>
|
|
80
|
+
<button type="button" class="danger" :aria-label="$t('media_kit.links.remove')" @click="removeLink(index)">×</button>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
<p v-else class="mk-links-empty">{{ $t('media_kit.links.empty') }}</p>
|
|
85
|
+
|
|
86
|
+
<div class="mk-links-footer">
|
|
87
|
+
<span v-if="draft.length && !canSave" class="mk-links-error">{{ $t('media_kit.links.invalid') }}</span>
|
|
88
|
+
<button type="button" class="mk-link-save" :disabled="!canSave" @click="save">
|
|
89
|
+
{{ saved ? $t('media_kit.links.saved') : $t('media_kit.links.save') }}
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</template>
|
|
94
|
+
|
|
95
|
+
<style scoped lang="scss">
|
|
96
|
+
.mk-links-editor { border: 1px solid rgba(140, 140, 160, .3); padding: 20px; background: rgba(255, 255, 255, .02); }
|
|
97
|
+
.mk-links-head { display: flex; justify-content: space-between; gap: 20px; align-items: start; }
|
|
98
|
+
.mk-links-head h3 { margin: 0 0 5px; font-size: 17px; }
|
|
99
|
+
.mk-links-head p, .mk-links-empty { margin: 0; color: #9292a4; font-size: 13px; line-height: 1.5; }
|
|
100
|
+
.mk-link-add, .mk-link-save { border: 1px solid #ffb000; background: #ffb000; color: #090909; min-height: 38px; padding: 0 16px; font-weight: 750; cursor: pointer; white-space: nowrap; }
|
|
101
|
+
button:disabled { opacity: .42; cursor: not-allowed; }
|
|
102
|
+
.mk-link-list { display: grid; gap: 10px; margin-top: 18px; }
|
|
103
|
+
.mk-link-row { display: grid; grid-template-columns: 30px minmax(150px, .75fr) minmax(220px, 1.4fr) auto; gap: 10px; align-items: end; padding: 12px; border: 1px solid rgba(140, 140, 160, .25); }
|
|
104
|
+
.mk-link-number { align-self: center; color: #ffb000; font-weight: 800; text-align: center; }
|
|
105
|
+
.mk-link-row label { display: grid; gap: 6px; color: #9292a4; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }
|
|
106
|
+
.mk-link-row input { min-width: 0; height: 40px; border: 1px solid rgba(140, 140, 160, .35); background: rgba(0, 0, 0, .2); color: inherit; padding: 0 11px; outline: none; }
|
|
107
|
+
.mk-link-row input:focus { border-color: #ffb000; }
|
|
108
|
+
.mk-link-actions { display: flex; gap: 5px; }
|
|
109
|
+
.mk-link-actions button { width: 36px; height: 40px; border: 1px solid rgba(140, 140, 160, .35); background: transparent; color: inherit; cursor: pointer; }
|
|
110
|
+
.mk-link-actions .danger { color: #ff7b7b; }
|
|
111
|
+
.mk-links-empty { padding: 22px 0 6px; }
|
|
112
|
+
.mk-links-footer { display: flex; align-items: center; justify-content: flex-end; gap: 14px; margin-top: 16px; }
|
|
113
|
+
.mk-links-error { color: #ff9898; font-size: 12px; }
|
|
114
|
+
|
|
115
|
+
@media (max-width: 760px) {
|
|
116
|
+
.mk-links-head { flex-direction: column; }
|
|
117
|
+
.mk-link-row { grid-template-columns: 24px 1fr; }
|
|
118
|
+
.mk-link-row label, .mk-link-actions { grid-column: 2; }
|
|
119
|
+
.mk-link-actions { justify-content: flex-end; }
|
|
120
|
+
}
|
|
121
|
+
</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const props = defineProps<{
|
|
3
|
+
store: Record<string, any>;
|
|
4
|
+
contactEmail?: string | null;
|
|
5
|
+
}>();
|
|
6
|
+
|
|
7
|
+
const emit = defineEmits<{
|
|
8
|
+
click: [type: string];
|
|
9
|
+
}>();
|
|
10
|
+
|
|
11
|
+
const products = computed(() => props.store?.products ?? []);
|
|
12
|
+
|
|
13
|
+
function formatPrice(product: Record<string, any>) {
|
|
14
|
+
try {
|
|
15
|
+
return new Intl.NumberFormat(undefined, {
|
|
16
|
+
style: 'currency',
|
|
17
|
+
currency: product.currency || props.store.currency || 'BRL',
|
|
18
|
+
}).format(Number(product.price || 0));
|
|
19
|
+
} catch {
|
|
20
|
+
return `${product.currency || props.store.currency || 'BRL'} ${Number(product.price || 0).toFixed(2)}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function interestUrl(product: Record<string, any>) {
|
|
25
|
+
if (!props.contactEmail) return null;
|
|
26
|
+
const subject = encodeURIComponent(`${props.store.name} — ${product.name}`);
|
|
27
|
+
return `mailto:${props.contactEmail}?subject=${subject}`;
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div class="mks">
|
|
33
|
+
<div class="mks__head">
|
|
34
|
+
<div class="mks__identity">
|
|
35
|
+
<img v-if="store.logo_url" :src="store.logo_url" :alt="store.name" />
|
|
36
|
+
<div>
|
|
37
|
+
<p class="mks__eyebrow">{{ $t('kit.media.store_eyebrow') }}</p>
|
|
38
|
+
<h2>{{ store.name }}</h2>
|
|
39
|
+
<p v-if="store.description">{{ store.description }}</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<span class="mks__badge">Mundo Gamer</span>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div v-if="products.length" class="mks__grid">
|
|
46
|
+
<article v-for="product in products" :key="product.id" class="mks__product">
|
|
47
|
+
<div class="mks__image">
|
|
48
|
+
<img v-if="product.image" :src="product.image" :alt="product.name" />
|
|
49
|
+
<span v-else>{{ product.name?.charAt(0) }}</span>
|
|
50
|
+
<em v-if="product.is_featured">{{ $t('kit.media.store_featured') }}</em>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="mks__body">
|
|
53
|
+
<div>
|
|
54
|
+
<h3>{{ product.name }}</h3>
|
|
55
|
+
<p v-if="product.description">{{ product.description }}</p>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="mks__footer">
|
|
58
|
+
<strong>{{ formatPrice(product) }}</strong>
|
|
59
|
+
<a
|
|
60
|
+
v-if="interestUrl(product)"
|
|
61
|
+
:href="interestUrl(product) || undefined"
|
|
62
|
+
@click="emit('click', 'store_product')"
|
|
63
|
+
>{{ $t('kit.media.store_interest') }}</a>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</article>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<p v-else class="mks__empty">{{ $t('kit.media.store_empty') }}</p>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<style scoped lang="scss">
|
|
74
|
+
.mks { display: grid; gap: 22px; }
|
|
75
|
+
.mks__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
|
|
76
|
+
.mks__identity { display: flex; align-items: center; gap: 16px; min-width: 0; }
|
|
77
|
+
.mks__identity img { width: 64px; height: 64px; object-fit: cover; border: 1px solid var(--kit-line); }
|
|
78
|
+
.mks__identity h2 { margin: 2px 0 4px; font-size: 24px; color: var(--kit-text); }
|
|
79
|
+
.mks__identity p { margin: 0; color: var(--kit-muted); line-height: 1.5; }
|
|
80
|
+
.mks__eyebrow { color: var(--kit-accent) !important; text-transform: uppercase; letter-spacing: .12em; font-size: 10px; font-weight: 800; }
|
|
81
|
+
.mks__badge { flex: none; padding: 6px 9px; border: 1px solid var(--kit-line); color: var(--kit-muted); font-size: 10px; text-transform: uppercase; letter-spacing: .08em; }
|
|
82
|
+
.mks__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
|
|
83
|
+
.mks__product { display: flex; flex-direction: column; min-height: 290px; overflow: hidden; border: 1px solid var(--kit-line); background: var(--kit-surface-2); }
|
|
84
|
+
.mks__image { position: relative; display: grid; place-items: center; aspect-ratio: 4 / 3; overflow: hidden; background: color-mix(in srgb, var(--kit-accent) 10%, var(--kit-bg)); color: var(--kit-accent); font-size: 38px; font-weight: 800; }
|
|
85
|
+
.mks__image img { width: 100%; height: 100%; object-fit: cover; transition: transform .25s ease; }
|
|
86
|
+
.mks__product:hover .mks__image img { transform: scale(1.035); }
|
|
87
|
+
.mks__image em { position: absolute; top: 10px; left: 10px; padding: 5px 7px; background: var(--kit-accent); color: #0c0e12; font-size: 9px; font-style: normal; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; }
|
|
88
|
+
.mks__body { display: flex; flex: 1; flex-direction: column; justify-content: space-between; gap: 18px; padding: 14px; }
|
|
89
|
+
.mks__body h3 { margin: 0 0 6px; color: var(--kit-text); font-size: 15px; }
|
|
90
|
+
.mks__body p { display: -webkit-box; margin: 0; overflow: hidden; color: var(--kit-muted); font-size: 12px; line-height: 1.45; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
|
|
91
|
+
.mks__footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
92
|
+
.mks__footer strong { color: var(--kit-text); font-size: 15px; }
|
|
93
|
+
.mks__footer a { padding: 7px 9px; border: 1px solid var(--kit-accent); color: var(--kit-accent); font-size: 11px; font-weight: 700; text-decoration: none; }
|
|
94
|
+
.mks__empty { margin: 0; color: var(--kit-muted); }
|
|
95
|
+
|
|
96
|
+
@media (max-width: 560px) {
|
|
97
|
+
.mks__head { flex-direction: column; }
|
|
98
|
+
.mks__badge { display: none; }
|
|
99
|
+
.mks__grid { grid-template-columns: 1fr 1fr; }
|
|
100
|
+
.mks__product { min-height: 250px; }
|
|
101
|
+
.mks__footer { align-items: flex-start; flex-direction: column; }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@media (max-width: 390px) {
|
|
105
|
+
.mks__grid { grid-template-columns: 1fr; }
|
|
106
|
+
}
|
|
107
|
+
</style>
|
|
@@ -13,8 +13,15 @@ const emit = defineEmits(["update:model"]);
|
|
|
13
13
|
const props = defineProps<SortInputProps>();
|
|
14
14
|
const sortModel = ref(props.model);
|
|
15
15
|
|
|
16
|
-
watch(
|
|
17
|
-
|
|
16
|
+
watch(
|
|
17
|
+
() => props.model,
|
|
18
|
+
(value) => {
|
|
19
|
+
sortModel.value = value;
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
watch(sortModel, (value) => {
|
|
24
|
+
emit("update:model", value);
|
|
18
25
|
});
|
|
19
26
|
|
|
20
27
|
const toggleSort = () => {
|
package/nuxt.config.ts
CHANGED
|
@@ -51,7 +51,14 @@ import { dirname } from "node:path";
|
|
|
51
51
|
const SHARED_UI_COMPONENT_PRIORITY = 1000;
|
|
52
52
|
|
|
53
53
|
const require = createRequire(`${process.cwd()}/`);
|
|
54
|
-
|
|
54
|
+
let packageRoot: string;
|
|
55
|
+
try {
|
|
56
|
+
packageRoot = dirname(require.resolve("@mundogamernetwork/shared-ui/package.json"));
|
|
57
|
+
} catch {
|
|
58
|
+
// The package does not resolve by its own published name while running the
|
|
59
|
+
// local playground directly from this repository.
|
|
60
|
+
packageRoot = process.cwd();
|
|
61
|
+
}
|
|
55
62
|
|
|
56
63
|
export default defineNuxtConfig({
|
|
57
64
|
components: [
|
|
@@ -92,8 +99,8 @@ export default defineNuxtConfig({
|
|
|
92
99
|
],
|
|
93
100
|
},
|
|
94
101
|
css: [
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
`${packageRoot}/assets/kit/kit-base.css`,
|
|
103
|
+
`${packageRoot}/assets/kit/kit-theme.css`,
|
|
97
104
|
],
|
|
98
105
|
runtimeConfig: {
|
|
99
106
|
public: {
|
package/package.json
CHANGED
|
@@ -123,6 +123,7 @@
|
|
|
123
123
|
:src="card.cover"
|
|
124
124
|
:alt="card.text"
|
|
125
125
|
:class="{ 'gray-out': card.isExpired || card.isUpcoming || !card.user_can_access }"
|
|
126
|
+
:style="{ objectPosition: `${card.coverFocalX * 100}% ${card.coverFocalY * 100}%` }"
|
|
126
127
|
@error="(e) => { (e.target as HTMLImageElement).src = '/imgs/no-cover-img.jpg' }"
|
|
127
128
|
/>
|
|
128
129
|
|
|
@@ -419,7 +420,7 @@ const sortMode = ref<"recent" | "popular">("recent")
|
|
|
419
420
|
// ------------------------------------------------------------------
|
|
420
421
|
// Data
|
|
421
422
|
// ------------------------------------------------------------------
|
|
422
|
-
const sliderImages = ref<Array<{ slug: string; cover: string; name: string; id: number }>>([])
|
|
423
|
+
const sliderImages = ref<Array<{ slug: string; cover: string; name: string; id: number; coverFocalX: number; coverFocalY: number }>>([])
|
|
423
424
|
const cards = ref<Array<any>>([])
|
|
424
425
|
const redeemedCards = ref<Array<any>>([])
|
|
425
426
|
|
|
@@ -529,11 +530,9 @@ async function loadFeaturedCampaigns() {
|
|
|
529
530
|
.filter((item: any) => (truthyFlag(item.featured) || truthyFlag(item.highlight)) && truthyFlag(item.is_open))
|
|
530
531
|
.map((item: any) => ({
|
|
531
532
|
id: item.id,
|
|
532
|
-
cover: item.game?.url_banner_src || item.
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
coverFocalX: item.game?.url_banner_src ? 0.5 : (item.cover_focal_x ?? 0.5),
|
|
536
|
-
coverFocalY: item.game?.url_banner_src ? 0.5 : (item.cover_focal_y ?? 0.5),
|
|
533
|
+
cover: item.cover_src || item.game?.url_banner_src || item.game?.url_cover_src || "/imgs/no-cover-img.jpg",
|
|
534
|
+
coverFocalX: item.cover_src ? (item.cover_focal_x ?? 0.5) : 0.5,
|
|
535
|
+
coverFocalY: item.cover_src ? (item.cover_focal_y ?? 0.5) : 0.5,
|
|
537
536
|
name: item.name,
|
|
538
537
|
slug: item.slug,
|
|
539
538
|
}))
|
|
@@ -592,6 +591,8 @@ async function loadCampaigns(page = 1) {
|
|
|
592
591
|
type: item.type?.name,
|
|
593
592
|
typeId: item.type?.id,
|
|
594
593
|
cover: item.cover_src || item.game?.url_cover_src || item.game?.url_banner_src || "/imgs/no-cover-img.jpg",
|
|
594
|
+
coverFocalX: item.cover_src ? (item.cover_focal_x ?? 0.5) : 0.5,
|
|
595
|
+
coverFocalY: item.cover_src ? (item.cover_focal_y ?? 0.5) : 0.5,
|
|
595
596
|
text: item.name,
|
|
596
597
|
time: item.time_to_expire ? item.time_to_expire.split(":")[0] : "0",
|
|
597
598
|
slug: item.slug,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { fetchPublicMediaKit, trackMediaKitEvent } from '../../services/mediaKitService';
|
|
3
3
|
import MediaKitWallBlock from '../../components/indie-wall/MediaKitWallBlock.vue';
|
|
4
|
+
import MgMediaKitStore from '../../components/MediaKit/MgMediaKitStore.vue';
|
|
4
5
|
|
|
5
6
|
const route = useRoute();
|
|
6
7
|
const slug = computed(() => route.params.slug as string);
|
|
@@ -25,6 +26,15 @@ const template = computed(() => kit.value?.template || 'creator');
|
|
|
25
26
|
const platforms = computed(() => kit.value?.platforms ?? []);
|
|
26
27
|
const totalFollowers = computed(() => kit.value?.total_followers ?? 0);
|
|
27
28
|
const isPremium = computed(() => kit.value?.is_premium ?? false);
|
|
29
|
+
const coverUrl = computed(() => kit.value?.cover_url || streamer.value?.cover || null);
|
|
30
|
+
const portfolioImages = computed(() => kit.value?.portfolio_images ?? streamer.value?.portfolio_images ?? []);
|
|
31
|
+
const brandLogos = computed(() => kit.value?.brand_logos ?? streamer.value?.brand_logos ?? []);
|
|
32
|
+
const creatorStore = computed(() => kit.value?.creator_store ?? null);
|
|
33
|
+
const customLinks = computed(() => kit.value?.custom_links ?? streamer.value?.custom_links ?? []);
|
|
34
|
+
|
|
35
|
+
const coverStyle = computed(() => coverUrl.value
|
|
36
|
+
? { backgroundImage: `linear-gradient(180deg, rgba(5,6,10,.14), rgba(5,6,10,.92)), url("${String(coverUrl.value).replace(/"/g, '\\"')}")` }
|
|
37
|
+
: undefined);
|
|
28
38
|
|
|
29
39
|
const PLATFORM_LABEL: Record<string, string> = {
|
|
30
40
|
instagram: 'Instagram', tiktok: 'TikTok', youtube: 'YouTube', twitch: 'Twitch', x: 'X',
|
|
@@ -109,7 +119,7 @@ function onDownload() {
|
|
|
109
119
|
<div v-else-if="error || !kit" class="kit-state"><p>{{ $t('kit.media.not_available') }}</p></div>
|
|
110
120
|
|
|
111
121
|
<template v-else>
|
|
112
|
-
<div class="mk-cover" />
|
|
122
|
+
<div class="mk-cover" :class="{ 'has-image': coverUrl }" :style="coverStyle" />
|
|
113
123
|
|
|
114
124
|
<div class="kit-wrap mk-sections">
|
|
115
125
|
<header class="mk-header">
|
|
@@ -118,6 +128,7 @@ function onDownload() {
|
|
|
118
128
|
|
|
119
129
|
<div class="mk-hid">
|
|
120
130
|
<h1>{{ streamer.name }}</h1>
|
|
131
|
+
<p v-if="streamer.custom_headline" class="mk-headline">{{ streamer.custom_headline }}</p>
|
|
121
132
|
<div class="mk-badges">
|
|
122
133
|
<span v-if="streamer.platform" class="mk-badge plat">{{ streamer.platform }}</span>
|
|
123
134
|
<span v-if="streamer.tier && streamer.tier !== 'free'" class="mk-badge tier">{{ String(streamer.tier).toUpperCase() }}</span>
|
|
@@ -132,6 +143,25 @@ function onDownload() {
|
|
|
132
143
|
</div>
|
|
133
144
|
</header>
|
|
134
145
|
|
|
146
|
+
<!-- Personal link hub: replaces a separate link-in-bio service. -->
|
|
147
|
+
<nav class="mk-link-hub no-print" data-sec="links" :aria-label="$t('kit.media.quick_links')">
|
|
148
|
+
<a v-for="(link, index) in customLinks" :key="`${link.url}-${index}`" class="mk-link" :class="{ 'mk-link--primary': index === 0 }" :href="link.url" target="_blank" rel="noopener" @click="track('click')">
|
|
149
|
+
<span>{{ link.label }}</span><b>↗</b>
|
|
150
|
+
</a>
|
|
151
|
+
<a v-if="streamer.contact_email" class="mk-link mk-link--primary" :href="`mailto:${streamer.contact_email}`" @click="track('contact')">
|
|
152
|
+
<span>{{ $t('kit.media.work_with_me') }}</span><b>↗</b>
|
|
153
|
+
</a>
|
|
154
|
+
<a v-if="creatorStore" class="mk-link" href="#store" @click="track('click')">
|
|
155
|
+
<span>{{ $t('kit.media.visit_store') }}</span><b>↓</b>
|
|
156
|
+
</a>
|
|
157
|
+
<a v-if="userId" class="mk-link" href="#support" @click="track('click')">
|
|
158
|
+
<span>{{ $t('kit.media.visit_mural') }}</span><b>↓</b>
|
|
159
|
+
</a>
|
|
160
|
+
<a v-for="s in socials" :key="s.platform" class="mk-link mk-link--social" :href="s.url" target="_blank" rel="noopener" @click="track('click')">
|
|
161
|
+
<span>{{ PLATFORM_LABEL[s.platform] || s.platform }}</span><b>↗</b>
|
|
162
|
+
</a>
|
|
163
|
+
</nav>
|
|
164
|
+
|
|
135
165
|
<!-- Reach / multi-platform followers -->
|
|
136
166
|
<section v-if="platforms.length" class="kit-block" data-sec="reach">
|
|
137
167
|
<h2>{{ $t('kit.media.reach') }}</h2>
|
|
@@ -151,6 +181,24 @@ function onDownload() {
|
|
|
151
181
|
</div>
|
|
152
182
|
</section>
|
|
153
183
|
|
|
184
|
+
<!-- Mundo Gamer creator store -->
|
|
185
|
+
<section v-if="creatorStore" id="store" class="kit-block kit-card mk-store-block" data-sec="store">
|
|
186
|
+
<MgMediaKitStore :store="creatorStore" :contact-email="streamer.contact_email" @click="track('click')" />
|
|
187
|
+
</section>
|
|
188
|
+
|
|
189
|
+
<!-- Creator-selected portfolio and brand imagery -->
|
|
190
|
+
<section v-if="portfolioImages.length || brandLogos.length" class="kit-block" data-sec="portfolio">
|
|
191
|
+
<h2>{{ $t('kit.media.portfolio') }}</h2>
|
|
192
|
+
<div v-if="portfolioImages.length" class="mk-portfolio">
|
|
193
|
+
<a v-for="(image, index) in portfolioImages" :key="image" :href="image" target="_blank" rel="noopener" @click="track('click')">
|
|
194
|
+
<img :src="image" :alt="`${streamer.name} — ${index + 1}`" />
|
|
195
|
+
</a>
|
|
196
|
+
</div>
|
|
197
|
+
<div v-if="brandLogos.length" class="mk-brand-logos">
|
|
198
|
+
<img v-for="logo in brandLogos" :key="logo" :src="logo" :alt="$t('kit.media.partner_brand')" />
|
|
199
|
+
</div>
|
|
200
|
+
</section>
|
|
201
|
+
|
|
154
202
|
<!-- TV streaming metrics -->
|
|
155
203
|
<section v-if="stats" class="kit-block" data-sec="metrics">
|
|
156
204
|
<h2>{{ $t('kit.media.key_metrics') }}</h2>
|
|
@@ -262,18 +310,8 @@ function onDownload() {
|
|
|
262
310
|
</div>
|
|
263
311
|
</section>
|
|
264
312
|
|
|
265
|
-
<!-- Social links -->
|
|
266
|
-
<section v-if="socials.length" class="kit-block no-print" data-sec="connect">
|
|
267
|
-
<h2>{{ $t('kit.media.connect') }}</h2>
|
|
268
|
-
<div class="mk-socials">
|
|
269
|
-
<a v-for="s in socials" :key="s.platform" class="mk-soc" :href="s.url" target="_blank" rel="noopener" @click="track('click')">
|
|
270
|
-
{{ s.platform }}
|
|
271
|
-
</a>
|
|
272
|
-
</div>
|
|
273
|
-
</section>
|
|
274
|
-
|
|
275
313
|
<!-- IndieWall / mural — self-contained widget (own fetch, realtime, stepper) -->
|
|
276
|
-
<section v-if="userId" class="kit-block" data-sec="support">
|
|
314
|
+
<section v-if="userId" id="support" class="kit-block" data-sec="support">
|
|
277
315
|
<MediaKitWallBlock :user-id="userId" />
|
|
278
316
|
</section>
|
|
279
317
|
|
|
@@ -308,8 +346,10 @@ function onDownload() {
|
|
|
308
346
|
|
|
309
347
|
/* Cover */
|
|
310
348
|
.mk-cover {
|
|
311
|
-
height:
|
|
349
|
+
height: 280px;
|
|
312
350
|
background: linear-gradient(120deg, var(--kit-accent-2, #6b15ac) 0%, #2a0e4e 50%, var(--kit-bg) 100%);
|
|
351
|
+
background-position: center;
|
|
352
|
+
background-size: cover;
|
|
313
353
|
position: relative;
|
|
314
354
|
border-bottom: 1px solid var(--kit-line);
|
|
315
355
|
|
|
@@ -319,44 +359,59 @@ function onDownload() {
|
|
|
319
359
|
inset: 0;
|
|
320
360
|
background: radial-gradient(circle at 80% 20%, rgba(168, 85, 247, .35), transparent 60%);
|
|
321
361
|
}
|
|
362
|
+
|
|
363
|
+
&.has-image::after {
|
|
364
|
+
background: linear-gradient(180deg, rgba(5, 6, 10, .04), rgba(5, 6, 10, .52));
|
|
365
|
+
}
|
|
322
366
|
}
|
|
323
367
|
|
|
324
368
|
/* Header */
|
|
325
369
|
.mk-header {
|
|
326
|
-
margin
|
|
370
|
+
margin: -82px auto 0;
|
|
327
371
|
position: relative;
|
|
328
372
|
z-index: 2;
|
|
329
373
|
display: flex;
|
|
330
|
-
|
|
331
|
-
align-items:
|
|
332
|
-
|
|
374
|
+
flex-direction: column;
|
|
375
|
+
align-items: center;
|
|
376
|
+
max-width: 760px;
|
|
377
|
+
text-align: center;
|
|
333
378
|
}
|
|
334
379
|
.mk-avatar {
|
|
335
|
-
width:
|
|
336
|
-
height:
|
|
380
|
+
width: 156px;
|
|
381
|
+
height: 156px;
|
|
337
382
|
border: 3px solid var(--kit-accent);
|
|
338
383
|
object-fit: cover;
|
|
339
384
|
flex-shrink: 0;
|
|
340
385
|
background: var(--kit-surface-2);
|
|
386
|
+
box-shadow: 0 20px 52px rgba(0, 0, 0, .42);
|
|
341
387
|
}
|
|
342
388
|
.mk-avatar-ph {
|
|
343
389
|
background: linear-gradient(135deg, var(--kit-accent-2, #6b15ac), var(--kit-accent));
|
|
344
390
|
}
|
|
345
391
|
.mk-hid {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
padding-bottom: 6px;
|
|
392
|
+
width: 100%;
|
|
393
|
+
padding-top: 18px;
|
|
349
394
|
|
|
350
395
|
h1 {
|
|
351
396
|
font-family: 'Rajdhani', sans-serif;
|
|
352
|
-
font-size:
|
|
397
|
+
font-size: clamp(36px, 7vw, 54px);
|
|
353
398
|
line-height: 1.05;
|
|
399
|
+
margin: 0;
|
|
354
400
|
}
|
|
355
401
|
}
|
|
402
|
+
.mk-headline {
|
|
403
|
+
margin: 8px auto 0;
|
|
404
|
+
max-width: 620px;
|
|
405
|
+
color: var(--kit-accent);
|
|
406
|
+
font-family: 'Rajdhani', sans-serif;
|
|
407
|
+
font-size: 20px;
|
|
408
|
+
font-weight: 600;
|
|
409
|
+
}
|
|
356
410
|
.mk-badges {
|
|
357
411
|
display: flex;
|
|
412
|
+
justify-content: center;
|
|
358
413
|
gap: 8px;
|
|
359
|
-
margin:
|
|
414
|
+
margin: 12px 0;
|
|
360
415
|
flex-wrap: wrap;
|
|
361
416
|
}
|
|
362
417
|
.mk-badge {
|
|
@@ -374,13 +429,60 @@ function onDownload() {
|
|
|
374
429
|
.mk-bio {
|
|
375
430
|
color: #c4c4d2;
|
|
376
431
|
font-size: 15px;
|
|
377
|
-
|
|
378
|
-
|
|
432
|
+
line-height: 1.65;
|
|
433
|
+
max-width: 66ch;
|
|
434
|
+
margin: 6px auto 0;
|
|
379
435
|
}
|
|
380
436
|
.mk-actions {
|
|
381
437
|
display: flex;
|
|
438
|
+
justify-content: center;
|
|
439
|
+
flex-wrap: wrap;
|
|
382
440
|
gap: 10px;
|
|
383
|
-
padding-
|
|
441
|
+
padding-top: 18px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/* Link-in-bio hub */
|
|
445
|
+
.mk-link-hub {
|
|
446
|
+
display: grid;
|
|
447
|
+
grid-template-columns: 1fr 1fr;
|
|
448
|
+
gap: 10px;
|
|
449
|
+
width: min(100%, 720px);
|
|
450
|
+
margin: 30px auto 2px;
|
|
451
|
+
}
|
|
452
|
+
.mk-link {
|
|
453
|
+
display: flex;
|
|
454
|
+
align-items: center;
|
|
455
|
+
justify-content: space-between;
|
|
456
|
+
gap: 16px;
|
|
457
|
+
min-height: 54px;
|
|
458
|
+
padding: 0 18px;
|
|
459
|
+
border: 1px solid var(--kit-line);
|
|
460
|
+
background: color-mix(in srgb, var(--kit-surface) 92%, transparent);
|
|
461
|
+
color: var(--kit-text);
|
|
462
|
+
font-size: 14px;
|
|
463
|
+
font-weight: 700;
|
|
464
|
+
text-decoration: none;
|
|
465
|
+
transition: transform .16s ease, border-color .16s ease, background .16s ease;
|
|
466
|
+
|
|
467
|
+
&:hover {
|
|
468
|
+
transform: translateY(-2px);
|
|
469
|
+
border-color: var(--kit-accent);
|
|
470
|
+
background: var(--kit-surface-2);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
b { color: var(--kit-accent); font-size: 16px; }
|
|
474
|
+
|
|
475
|
+
&--primary {
|
|
476
|
+
grid-column: 1 / -1;
|
|
477
|
+
background: var(--kit-accent);
|
|
478
|
+
border-color: var(--kit-accent);
|
|
479
|
+
color: var(--kit-accent-ink);
|
|
480
|
+
|
|
481
|
+
b { color: var(--kit-accent-ink); }
|
|
482
|
+
&:hover { background: var(--kit-accent); filter: brightness(1.06); }
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
&--social { min-height: 46px; color: var(--kit-muted); }
|
|
384
486
|
}
|
|
385
487
|
|
|
386
488
|
/* Buttons */
|
|
@@ -434,6 +536,29 @@ function onDownload() {
|
|
|
434
536
|
}
|
|
435
537
|
}
|
|
436
538
|
}
|
|
539
|
+
.mk-store-block { padding: 24px; scroll-margin-top: 24px; }
|
|
540
|
+
.mk-portfolio {
|
|
541
|
+
display: grid;
|
|
542
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
543
|
+
gap: 10px;
|
|
544
|
+
|
|
545
|
+
a { display: block; overflow: hidden; aspect-ratio: 4 / 3; border: 1px solid var(--kit-line); background: var(--kit-surface); }
|
|
546
|
+
img { width: 100%; height: 100%; object-fit: cover; transition: transform .2s ease; }
|
|
547
|
+
a:hover img { transform: scale(1.03); }
|
|
548
|
+
}
|
|
549
|
+
.mk-brand-logos {
|
|
550
|
+
display: flex;
|
|
551
|
+
align-items: center;
|
|
552
|
+
justify-content: center;
|
|
553
|
+
flex-wrap: wrap;
|
|
554
|
+
gap: 16px;
|
|
555
|
+
margin-top: 16px;
|
|
556
|
+
padding: 18px;
|
|
557
|
+
border: 1px solid var(--kit-line);
|
|
558
|
+
background: var(--kit-surface);
|
|
559
|
+
|
|
560
|
+
img { width: 112px; height: 54px; object-fit: contain; filter: grayscale(1); opacity: .8; }
|
|
561
|
+
}
|
|
437
562
|
.mk-period {
|
|
438
563
|
color: var(--kit-muted);
|
|
439
564
|
font-size: 13px;
|
|
@@ -768,16 +893,19 @@ function onDownload() {
|
|
|
768
893
|
.mk-sections { display: flex; flex-direction: column; }
|
|
769
894
|
.mk-sections > .mk-header { order: 0; }
|
|
770
895
|
.mk-sections > .mk-footer { order: 100; }
|
|
771
|
-
.mk-sections > [data-sec="
|
|
772
|
-
.mk-sections > [data-sec="
|
|
773
|
-
.mk-sections > [data-sec="
|
|
774
|
-
.mk-sections > [data-sec="
|
|
775
|
-
.mk-sections > [data-sec="
|
|
776
|
-
.mk-sections > [data-sec="
|
|
777
|
-
.mk-sections > [data-sec="
|
|
778
|
-
.mk-sections > [data-sec="
|
|
779
|
-
.mk-sections > [data-sec="
|
|
780
|
-
.mk-sections > [data-sec="
|
|
896
|
+
.mk-sections > [data-sec="links"] { order: 1; }
|
|
897
|
+
.mk-sections > [data-sec="reach"] { order: 2; }
|
|
898
|
+
.mk-sections > [data-sec="store"] { order: 3; }
|
|
899
|
+
.mk-sections > [data-sec="portfolio"] { order: 4; }
|
|
900
|
+
.mk-sections > [data-sec="metrics"] { order: 5; }
|
|
901
|
+
.mk-sections > [data-sec="audience"] { order: 6; }
|
|
902
|
+
.mk-sections > [data-sec="styles"] { order: 7; }
|
|
903
|
+
.mk-sections > [data-sec="games"] { order: 8; }
|
|
904
|
+
.mk-sections > [data-sec="rates"] { order: 9; }
|
|
905
|
+
.mk-sections > [data-sec="brands"] { order: 10; }
|
|
906
|
+
.mk-sections > [data-sec="schedule"] { order: 11; }
|
|
907
|
+
.mk-sections > [data-sec="connect"] { order: 12; }
|
|
908
|
+
.mk-sections > [data-sec="support"] { order: 13; scroll-margin-top: 24px; }
|
|
781
909
|
|
|
782
910
|
[data-template="performance"] {
|
|
783
911
|
.mk-sections > [data-sec="metrics"] { order: 1; }
|
|
@@ -803,7 +931,20 @@ function onDownload() {
|
|
|
803
931
|
.mk-cols2, .mk-rates, .mk-bundles { grid-template-columns: 1fr; }
|
|
804
932
|
.mk-brands { grid-template-columns: repeat(3, 1fr); }
|
|
805
933
|
.mk-schedule { grid-template-columns: repeat(4, 1fr); }
|
|
806
|
-
.mk-
|
|
934
|
+
.mk-portfolio { grid-template-columns: 1fr 1fr; }
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
@media (max-width: 560px) {
|
|
938
|
+
.kit-wrap { padding-inline: 14px; }
|
|
939
|
+
.mk-cover { height: 230px; }
|
|
940
|
+
.mk-avatar { width: 132px; height: 132px; }
|
|
941
|
+
.mk-header { margin-top: -68px; }
|
|
942
|
+
.mk-link-hub { grid-template-columns: 1fr; }
|
|
943
|
+
.mk-link--primary { grid-column: auto; }
|
|
944
|
+
.mk-actions { width: 100%; }
|
|
945
|
+
.kit-btn { flex: 1; justify-content: center; }
|
|
946
|
+
.mk-portfolio { grid-template-columns: 1fr; }
|
|
947
|
+
.mk-store-block { padding: 16px; }
|
|
807
948
|
}
|
|
808
949
|
|
|
809
950
|
@media print {
|
|
@@ -811,7 +952,7 @@ function onDownload() {
|
|
|
811
952
|
}
|
|
812
953
|
|
|
813
954
|
// ── Scroll unlock (body has overflow-y: hidden globally in some frontends) ───
|
|
814
|
-
:global(body):has(.
|
|
955
|
+
:global(body):has(.kit-media) {
|
|
815
956
|
overflow-y: auto !important;
|
|
816
957
|
max-height: none !important;
|
|
817
958
|
}
|