@mundogamernetwork/shared-ui 1.0.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/README.md +283 -0
- package/components/PressKit/AssetGallery.vue +349 -0
- package/components/PressKit/Awards.vue +100 -0
- package/components/PressKit/Credits.vue +78 -0
- package/components/PressKit/FactSheet.vue +204 -0
- package/components/PressKit/Hero.vue +143 -0
- package/components/PressKit/Quotes.vue +80 -0
- package/components/PressKit/VideoPlayer.vue +134 -0
- package/components/checkout/MgCartItemList.vue +214 -0
- package/components/checkout/MgCartSummary.vue +204 -0
- package/components/checkout/MgCheckoutSidebar.vue +230 -0
- package/components/checkout/MgGuestEmailForm.vue +97 -0
- package/components/checkout/MgPaymentMethodSelector.vue +162 -0
- package/components/checkout/MgPixQRCode.vue +222 -0
- package/components/indie-wall/IndieWallLeaderboard.vue +208 -0
- package/components/indie-wall/MuralCanvas.vue +481 -0
- package/components/indie-wall/StepBlock.vue +314 -0
- package/components/indie-wall/StepCustomize.vue +530 -0
- package/components/indie-wall/StepGoal.vue +169 -0
- package/components/indie-wall/StepPackage.vue +145 -0
- package/components/indie-wall/StepPay.vue +209 -0
- package/components/indie-wall/SupportStepper.vue +372 -0
- package/components/invoices/MgInvoiceDownload.vue +50 -0
- package/components/pricing/MgBillingToggle.vue +74 -0
- package/components/pricing/MgPricingCard.vue +245 -0
- package/components/ui/Header/MgMessageCard.vue +147 -0
- package/components/ui/Header/MgMessageModal.vue +414 -0
- package/components/ui/Header/MgNotificationCard.vue +200 -0
- package/components/ui/Header/MgNotificationsModal.vue +125 -0
- package/components/ui/MgAnnouncementBanner.vue +147 -0
- package/components/ui/MgBanners.vue +23 -0
- package/components/ui/MgHeaderComponent.vue +283 -0
- package/components/ui/MgHeaderUIConfig.vue +225 -0
- package/components/ui/MgHeaderUIUser.vue +301 -0
- package/components/ui/MgLoginModal.vue +156 -0
- package/components/ui/MgPromotionBanner.vue +185 -0
- package/composables/useLogout.ts +42 -0
- package/composables/useMgCheckout.ts +287 -0
- package/composables/useMgUserNotifications.ts +122 -0
- package/composables/usePaymentMethods.ts +75 -0
- package/composables/useSubscription.ts +163 -0
- package/middleware/auth.global.ts +40 -0
- package/nuxt.config.ts +31 -0
- package/package.json +40 -0
- package/pages/[slug]/index.vue +112 -0
- package/pages/about.vue +133 -0
- package/pages/blog.vue +430 -0
- package/pages/careers.vue +329 -0
- package/pages/contact.vue +339 -0
- package/pages/faq.vue +317 -0
- package/pages/health-check.vue +20 -0
- package/pages/icons.vue +58 -0
- package/pages/magazine/[slug].vue +209 -0
- package/pages/magazine/index.vue +267 -0
- package/pages/media-kit/[slug].vue +625 -0
- package/pages/mural/[slug].vue +1058 -0
- package/pages/partners.vue +290 -0
- package/pages/press.vue +237 -0
- package/pages/presskit/[slug].vue +191 -0
- package/pages/roadmap.vue +355 -0
- package/pages/status.vue +199 -0
- package/pages/team.vue +266 -0
- package/pages/wall/[slug].vue +11 -0
- package/plugins/auth.client.ts +17 -0
- package/plugins/echo.client.ts +132 -0
- package/services/authService.ts +95 -0
- package/services/chatService.ts +53 -0
- package/services/contactService.ts +35 -0
- package/services/documentService.ts +16 -0
- package/services/httpService.ts +95 -0
- package/services/indieWallService.ts +174 -0
- package/services/institutionalService.ts +248 -0
- package/services/mediaKitService.ts +51 -0
- package/services/notificationsService.ts +20 -0
- package/services/pressKitService.ts +55 -0
- package/stores/announcement.ts +129 -0
- package/stores/auth.ts +86 -0
- package/stores/chat.ts +150 -0
- package/stores/contact.ts +28 -0
- package/stores/document.ts +27 -0
- package/stores/index.ts +34 -0
- package/stores/institutional.ts +231 -0
- package/stores/login.ts +27 -0
- package/stores/notifications.ts +133 -0
- package/stores/promotion.ts +154 -0
- package/types/index.ts +135 -0
- package/utils/serialize.ts +29 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { ref, computed } from "vue";
|
|
2
|
+
import type { AxiosInstance } from "axios";
|
|
3
|
+
|
|
4
|
+
export interface PlanPricing {
|
|
5
|
+
id: number;
|
|
6
|
+
value: number;
|
|
7
|
+
currency?: { iso: string; symbol: string };
|
|
8
|
+
payment_period_id: number;
|
|
9
|
+
payment_period?: { id: number; name: string };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Plan {
|
|
13
|
+
id: number;
|
|
14
|
+
name: string;
|
|
15
|
+
slug: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
trial_days?: number;
|
|
18
|
+
popular?: boolean;
|
|
19
|
+
business?: boolean;
|
|
20
|
+
plan_pricings?: { data: PlanPricing[] };
|
|
21
|
+
plan_benefits?: { data: any[] };
|
|
22
|
+
feature_limits?: Record<string, any>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SubscriptionData {
|
|
26
|
+
id: number;
|
|
27
|
+
active: boolean;
|
|
28
|
+
cancel_at?: string | null;
|
|
29
|
+
next_payment?: string | null;
|
|
30
|
+
mg_network_plan?: { data: Plan };
|
|
31
|
+
mg_network_plan_pricing?: { data: PlanPricing };
|
|
32
|
+
subscription_gateway_id?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useSubscription(
|
|
36
|
+
httpService: AxiosInstance,
|
|
37
|
+
httpServiceMain?: AxiosInstance
|
|
38
|
+
) {
|
|
39
|
+
const mainHttp = httpServiceMain ?? httpService;
|
|
40
|
+
|
|
41
|
+
const plans = ref<Plan[]>([]);
|
|
42
|
+
const subscription = ref<SubscriptionData | null>(null);
|
|
43
|
+
const loading = ref(false);
|
|
44
|
+
const error = ref<string | null>(null);
|
|
45
|
+
|
|
46
|
+
async function fetchPlans(systemId?: number) {
|
|
47
|
+
loading.value = true;
|
|
48
|
+
try {
|
|
49
|
+
const params: Record<string, any> = {};
|
|
50
|
+
if (systemId) {
|
|
51
|
+
params["filter[mg_network_system_id]"] = systemId;
|
|
52
|
+
}
|
|
53
|
+
const res = await mainHttp.get("/network-plans", { params });
|
|
54
|
+
plans.value = res.data?.data ?? [];
|
|
55
|
+
} catch (e: any) {
|
|
56
|
+
error.value = e?.message ?? "Failed to load plans";
|
|
57
|
+
} finally {
|
|
58
|
+
loading.value = false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Fetch subscription status. Tries auth store first (populated by /auth/user),
|
|
64
|
+
* falls back to /me/subscription endpoint for backward compatibility.
|
|
65
|
+
*/
|
|
66
|
+
async function fetchMySubscription() {
|
|
67
|
+
// Try to read from auth store first (zero network calls)
|
|
68
|
+
try {
|
|
69
|
+
const pinia = (await import("pinia")).getActivePinia?.();
|
|
70
|
+
if (pinia) {
|
|
71
|
+
const authStore = pinia.state.value["auth-store"];
|
|
72
|
+
if (authStore?.user?.subscription) {
|
|
73
|
+
subscription.value = authStore.user.subscription;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// Auth store not available — fall through to API call
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
loading.value = true;
|
|
82
|
+
try {
|
|
83
|
+
const res = await httpService.get("/me/subscription");
|
|
84
|
+
subscription.value = res.data?.data ?? res.data ?? null;
|
|
85
|
+
} catch {
|
|
86
|
+
subscription.value = null;
|
|
87
|
+
} finally {
|
|
88
|
+
loading.value = false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function subscribe(
|
|
93
|
+
pricingId: number,
|
|
94
|
+
successUrl: string,
|
|
95
|
+
cancelUrl: string,
|
|
96
|
+
paymentGateway?: string,
|
|
97
|
+
discountCouponCode?: string
|
|
98
|
+
): Promise<string | null> {
|
|
99
|
+
loading.value = true;
|
|
100
|
+
error.value = null;
|
|
101
|
+
try {
|
|
102
|
+
const params: Record<string, any> = {
|
|
103
|
+
mg_network_plan_pricing_id: pricingId,
|
|
104
|
+
success_url: successUrl,
|
|
105
|
+
cancel_url: cancelUrl,
|
|
106
|
+
};
|
|
107
|
+
if (paymentGateway) params.payment_gateway = paymentGateway;
|
|
108
|
+
if (discountCouponCode) params.discount_coupon_code = discountCouponCode;
|
|
109
|
+
|
|
110
|
+
const res = await mainHttp.post("/public/plans/subscriptions", params);
|
|
111
|
+
const payerAction = res.data?.meta?.payer_action ?? res.data?.payer_action;
|
|
112
|
+
|
|
113
|
+
if (payerAction) {
|
|
114
|
+
window.location.href = payerAction;
|
|
115
|
+
}
|
|
116
|
+
return payerAction ?? null;
|
|
117
|
+
} catch (e: any) {
|
|
118
|
+
error.value = e?.response?.data?.message ?? "Subscription failed";
|
|
119
|
+
return null;
|
|
120
|
+
} finally {
|
|
121
|
+
loading.value = false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function cancelSubscription(subscriptionId: number | string) {
|
|
126
|
+
loading.value = true;
|
|
127
|
+
error.value = null;
|
|
128
|
+
try {
|
|
129
|
+
await mainHttp.delete(`/public/plans/subscriptions/${subscriptionId}`);
|
|
130
|
+
if (subscription.value) {
|
|
131
|
+
subscription.value.active = false;
|
|
132
|
+
subscription.value.cancel_at = new Date().toISOString();
|
|
133
|
+
}
|
|
134
|
+
} catch (e: any) {
|
|
135
|
+
error.value = e?.response?.data?.message ?? "Failed to cancel subscription";
|
|
136
|
+
} finally {
|
|
137
|
+
loading.value = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Computed
|
|
142
|
+
const isActive = computed(() => subscription.value?.active === true);
|
|
143
|
+
const isCancelled = computed(() => !!subscription.value?.cancel_at);
|
|
144
|
+
const currentPlan = computed(
|
|
145
|
+
() => subscription.value?.mg_network_plan?.data ?? null
|
|
146
|
+
);
|
|
147
|
+
const currentPlanName = computed(() => currentPlan.value?.name ?? null);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
plans,
|
|
151
|
+
subscription,
|
|
152
|
+
loading,
|
|
153
|
+
error,
|
|
154
|
+
fetchPlans,
|
|
155
|
+
fetchMySubscription,
|
|
156
|
+
subscribe,
|
|
157
|
+
cancelSubscription,
|
|
158
|
+
isActive,
|
|
159
|
+
isCancelled,
|
|
160
|
+
currentPlan,
|
|
161
|
+
currentPlanName,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export default defineNuxtRouteMiddleware(async (to) => {
|
|
2
|
+
try {
|
|
3
|
+
const nuxtApp = useNuxtApp();
|
|
4
|
+
const pinia = (nuxtApp as any).$pinia;
|
|
5
|
+
if (!pinia) return;
|
|
6
|
+
const runtimeConfig = useRuntimeConfig();
|
|
7
|
+
const authStore = useAuthStore(pinia);
|
|
8
|
+
|
|
9
|
+
const locale = nuxtApp.$i18n?.locale?.value ?? "en";
|
|
10
|
+
const { signedIn } = storeToRefs(authStore);
|
|
11
|
+
|
|
12
|
+
const accountsBaseUrl = runtimeConfig.public.mgSharedUi?.accountsBaseUrl || runtimeConfig.public.accountsBaseUrl;
|
|
13
|
+
const configuredRoutes: string[] = runtimeConfig.public.mgSharedUi?.protectedRoutes || [];
|
|
14
|
+
|
|
15
|
+
// No protected routes configured — skip entirely
|
|
16
|
+
if (!configuredRoutes.length) return;
|
|
17
|
+
|
|
18
|
+
// Build regex patterns from configured protected routes
|
|
19
|
+
const protectedRoutePatterns: RegExp[] = configuredRoutes.map(
|
|
20
|
+
(route) => new RegExp(`^/(${locale})${route}(/.*)?$`),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const isProtectedRoute = protectedRoutePatterns.some((pattern) => pattern.test(to.path));
|
|
24
|
+
|
|
25
|
+
if (isProtectedRoute) {
|
|
26
|
+
if (!signedIn.value) {
|
|
27
|
+
await authStore.getUser();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!signedIn.value) {
|
|
31
|
+
const currentUrl = typeof window !== "undefined" ? encodeURIComponent(window.location.href) : "";
|
|
32
|
+
const loginUrl = `${accountsBaseUrl}/login?redirect_to=${currentUrl}`;
|
|
33
|
+
return navigateTo(loginUrl, { external: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
// Silently fail — don't block rendering if auth system isn't available
|
|
38
|
+
console.warn("[shared-ui Auth Middleware] Error:", e);
|
|
39
|
+
}
|
|
40
|
+
});
|
package/nuxt.config.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export default defineNuxtConfig({
|
|
2
|
+
runtimeConfig: {
|
|
3
|
+
public: {
|
|
4
|
+
mgSharedUi: {
|
|
5
|
+
platform: "", // e.g. MGC, MGTV, MGN
|
|
6
|
+
systemId: "", // VITE_SYSTEM_ID for notification filtering
|
|
7
|
+
apiBaseURL: "", // VITE_API_BASE_URL
|
|
8
|
+
accountsBaseUrl: "", // VITE_BASE_ACCOUNTS_URL
|
|
9
|
+
networkBaseUrl: "", // VITE_BASE_URL_NETWORK (institutional APIs)
|
|
10
|
+
features: {
|
|
11
|
+
chat: true,
|
|
12
|
+
notifications: true,
|
|
13
|
+
wallet: true,
|
|
14
|
+
darkMode: true,
|
|
15
|
+
search: false,
|
|
16
|
+
statusOnline: true,
|
|
17
|
+
},
|
|
18
|
+
languages: ["pt-BR", "en", "es", "de", "ro"],
|
|
19
|
+
protectedRoutes: [] as string[],
|
|
20
|
+
chatEndpoints: {
|
|
21
|
+
list: "/api/v1/chats",
|
|
22
|
+
send: "/api/v1/chats/messages/",
|
|
23
|
+
show: "/api/v1/chats/{chatId}",
|
|
24
|
+
unread: "api/v1/chats/messages/unread/count",
|
|
25
|
+
delete: "/api/v1/chats/{chatId}",
|
|
26
|
+
deleteMessage: "/api/v1/chats/messages/{messageId}",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mundogamernetwork/shared-ui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Mundo Gamer Network - Shared UI Layer (Nuxt 3)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./nuxt.config.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"components",
|
|
9
|
+
"composables",
|
|
10
|
+
"middleware",
|
|
11
|
+
"pages",
|
|
12
|
+
"plugins",
|
|
13
|
+
"services",
|
|
14
|
+
"stores",
|
|
15
|
+
"types",
|
|
16
|
+
"utils",
|
|
17
|
+
"nuxt.config.ts"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "nuxi dev .playground",
|
|
24
|
+
"build": "nuxi build .playground"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@pinia/nuxt": ">=0.5.0",
|
|
28
|
+
"@pinia-plugin-persistedstate/nuxt": ">=1.0.0",
|
|
29
|
+
"axios": ">=1.0.0",
|
|
30
|
+
"laravel-echo": ">=1.15.0",
|
|
31
|
+
"nuxt": ">=3.13.0",
|
|
32
|
+
"pinia": ">=2.1.0",
|
|
33
|
+
"pusher-js": ">=8.0.0",
|
|
34
|
+
"vue": ">=3.5.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"nuxt": "^3.15.4",
|
|
38
|
+
"typescript": "^5.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { storeToRefs } from "pinia";
|
|
3
|
+
|
|
4
|
+
definePageMeta({ layout: "default" });
|
|
5
|
+
|
|
6
|
+
const route = useRoute();
|
|
7
|
+
const locale = useNuxtApp().$i18n.locale;
|
|
8
|
+
const slug = route.params.slug;
|
|
9
|
+
|
|
10
|
+
const config = useRuntimeConfig();
|
|
11
|
+
const systemId = config.public.mgSharedUi?.systemId || import.meta.env.VITE_SYSTEM_ID;
|
|
12
|
+
|
|
13
|
+
const documentStore = useDocumentStore();
|
|
14
|
+
const { getDocument } = storeToRefs(documentStore);
|
|
15
|
+
|
|
16
|
+
const { error } = await useAsyncData(`document-${slug}`, async () => {
|
|
17
|
+
await documentStore.fetchDocument({
|
|
18
|
+
filter: {
|
|
19
|
+
slug: slug,
|
|
20
|
+
platform_id: systemId,
|
|
21
|
+
},
|
|
22
|
+
lang: locale.value,
|
|
23
|
+
});
|
|
24
|
+
return getDocument.value?.data;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (error.value || !getDocument.value?.data?.localized_content) {
|
|
28
|
+
throw createError({ statusCode: 404, statusMessage: "Page Not Found" });
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div id="institution-page" class="container institution-page">
|
|
34
|
+
<section>
|
|
35
|
+
<div class="header">
|
|
36
|
+
<div v-if="getDocument && getDocument.data && getDocument.data.content">
|
|
37
|
+
<h1 class="title-4">{{ getDocument.data.type_document?.localized_type }}</h1>
|
|
38
|
+
<p>{{ $t("more.document.last_updated") }} {{ getDocument.data.updated_at }}</p>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="container">
|
|
42
|
+
<div class="row">
|
|
43
|
+
<div
|
|
44
|
+
v-if="getDocument && getDocument.data && getDocument.data.localized_content"
|
|
45
|
+
class="col-8 offset-2"
|
|
46
|
+
>
|
|
47
|
+
<div v-html="getDocument.data.localized_content"></div>
|
|
48
|
+
</div>
|
|
49
|
+
<div v-else>{{ $t("components.loading") }}</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</section>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<style lang="scss" scoped>
|
|
57
|
+
.institution-page {
|
|
58
|
+
color: var(--inactive);
|
|
59
|
+
font-size: 0.875rem;
|
|
60
|
+
|
|
61
|
+
.header {
|
|
62
|
+
position: relative;
|
|
63
|
+
height: 200px;
|
|
64
|
+
display: flex;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
align-items: center;
|
|
67
|
+
font-size: 0.875rem;
|
|
68
|
+
margin-bottom: 2rem;
|
|
69
|
+
|
|
70
|
+
& > div {
|
|
71
|
+
width: 480px;
|
|
72
|
+
text-align: center;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
&:before {
|
|
76
|
+
content: " ";
|
|
77
|
+
display: block;
|
|
78
|
+
position: absolute;
|
|
79
|
+
background: url("@/assets/images/bg-privacy-policy.png") no-repeat center center / cover;
|
|
80
|
+
width: 100%;
|
|
81
|
+
height: 100%;
|
|
82
|
+
top: 50%;
|
|
83
|
+
transform: translateY(-50%);
|
|
84
|
+
z-index: -1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
:deep(a) {
|
|
89
|
+
color: var(--chip-text);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
:deep(h2), :deep(h3), :deep(h4) {
|
|
93
|
+
color: var(--card-cover-title);
|
|
94
|
+
margin: 16px 0 8px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
:deep(p) {
|
|
98
|
+
line-height: 20px;
|
|
99
|
+
margin-bottom: 8px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
:deep(ul), :deep(ol) {
|
|
103
|
+
padding-left: 20px;
|
|
104
|
+
margin-bottom: 12px;
|
|
105
|
+
|
|
106
|
+
li {
|
|
107
|
+
line-height: 20px;
|
|
108
|
+
margin-bottom: 4px;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
</style>
|
package/pages/about.vue
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
definePageMeta({ layout: "default" });
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div id="about" class="container about">
|
|
7
|
+
<section>
|
|
8
|
+
<div class="header">
|
|
9
|
+
<div>
|
|
10
|
+
<h1 class="title-4">{{ $t("more.about.title") }}</h1>
|
|
11
|
+
<p>{{ $t("more.about.subtitle") }}</p>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="container">
|
|
15
|
+
<div class="row">
|
|
16
|
+
<div class="col-8 offset-2">
|
|
17
|
+
<div class="content">
|
|
18
|
+
<div class="section-block">
|
|
19
|
+
<h2>{{ $t("more.about.section_1_title") }}</h2>
|
|
20
|
+
<p>{{ $t("more.about.section_1_text") }}</p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="section-block">
|
|
24
|
+
<h2>{{ $t("more.about.section_2_title") }}</h2>
|
|
25
|
+
<p>{{ $t("more.about.section_2_text") }}</p>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="benefits" v-if="$t('more.about.benefits_title')">
|
|
29
|
+
<h3>{{ $t("more.about.benefits_title") }}</h3>
|
|
30
|
+
<ul>
|
|
31
|
+
<li v-for="n in 6" :key="n">
|
|
32
|
+
<template v-if="$te(`more.about.benefit_${n}`)">
|
|
33
|
+
{{ $t(`more.about.benefit_${n}`) }}
|
|
34
|
+
</template>
|
|
35
|
+
</li>
|
|
36
|
+
</ul>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="section-block">
|
|
40
|
+
<h2>{{ $t("more.about.section_3_title") }}</h2>
|
|
41
|
+
<p>{{ $t("more.about.section_3_text") }}</p>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</section>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<style lang="scss" scoped>
|
|
52
|
+
.about {
|
|
53
|
+
color: var(--inactive);
|
|
54
|
+
font-size: 0.875rem;
|
|
55
|
+
|
|
56
|
+
.header {
|
|
57
|
+
position: relative;
|
|
58
|
+
height: 200px;
|
|
59
|
+
display: flex;
|
|
60
|
+
justify-content: center;
|
|
61
|
+
align-items: center;
|
|
62
|
+
font-size: 0.875rem;
|
|
63
|
+
margin-bottom: 2rem;
|
|
64
|
+
|
|
65
|
+
& > div {
|
|
66
|
+
width: 480px;
|
|
67
|
+
text-align: center;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&:before {
|
|
71
|
+
content: " ";
|
|
72
|
+
display: block;
|
|
73
|
+
position: absolute;
|
|
74
|
+
background: url("@/assets/images/bg-about.png") no-repeat center center / cover;
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
top: 50%;
|
|
78
|
+
transform: translateY(-50%);
|
|
79
|
+
z-index: -1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.content {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
gap: 32px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.section-block {
|
|
90
|
+
h2 {
|
|
91
|
+
font-size: 18px;
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
line-height: 26px;
|
|
94
|
+
color: var(--card-cover-title);
|
|
95
|
+
margin-bottom: 8px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
p {
|
|
99
|
+
font-size: 14px;
|
|
100
|
+
font-weight: 400;
|
|
101
|
+
line-height: 20px;
|
|
102
|
+
color: var(--inactive);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.benefits {
|
|
107
|
+
h3 {
|
|
108
|
+
font-size: 16px;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
color: var(--card-cover-title);
|
|
111
|
+
margin-bottom: 12px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
ul {
|
|
115
|
+
list-style: disc;
|
|
116
|
+
padding-left: 20px;
|
|
117
|
+
|
|
118
|
+
li {
|
|
119
|
+
font-size: 14px;
|
|
120
|
+
line-height: 24px;
|
|
121
|
+
color: var(--inactive);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@media screen and (max-width: 768px) {
|
|
128
|
+
.about .header > div {
|
|
129
|
+
width: 100%;
|
|
130
|
+
padding: 0 16px;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
</style>
|