diff --git a/layers/assets/css/components/_modal.css b/layers/assets/css/components/_modal.css
index 00102ec..f8d9f0a 100644
--- a/layers/assets/css/components/_modal.css
+++ b/layers/assets/css/components/_modal.css
@@ -19,4 +19,8 @@
.content-text {
@apply text-center text-[15px] text-[#333333] leading-6 tracking-[-0.45px];
}
+
+ .content-text .highlight {
+ @apply text-[#FC4420];
+ }
}
diff --git a/layers/components/atoms/Button/Circle.vue b/layers/components/atoms/Button/Circle.vue
index 7d79c0c..c6e6cd6 100644
--- a/layers/components/atoms/Button/Circle.vue
+++ b/layers/components/atoms/Button/Circle.vue
@@ -45,6 +45,6 @@ const componentProps = computed(() => {
w-[40px] h-[40px] md:w-[48px] md:h-[48px]
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-[rgba(255,255,255,0.06)] before:rounded-full
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-full after:opacity-0 after:transition-all after:duration-300 after:ease-in-out
-hover:after:opacity-10;
+ hover:after:opacity-10;
}
diff --git a/layers/components/layouts/Header.vue b/layers/components/layouts/Header.vue
index 1490ca0..c0f0caa 100644
--- a/layers/components/layouts/Header.vue
+++ b/layers/components/layouts/Header.vue
@@ -6,12 +6,17 @@ import type {
GameDataMenuChildren,
GameDataResourceGroup,
GameDataResourceGroupSet,
+ PlatformTransformType,
} from '#layers/types/api/gameData'
const route = useRoute()
+const { tm } = useI18n()
const { width } = useWindowSize()
+const device = useDevice()
const gameDataStore = useGameDataStore()
const scrollStore = useScrollStore()
+const breakpoints = useResponsiveBreakpoints()
+const modalStore = useModalStore()
const { gameData } = storeToRefs(gameDataStore)
const { isPassedStoveGnb } = storeToRefs(scrollStore)
@@ -33,6 +38,12 @@ const gnb2depthButtonData = computed(
() => gnbData?.buttons[1]?.button_json as GameDataResourceGroupSet
)
const currentPath = computed(() => formatPathWithoutLocale(route.path))
+const supportedPlatforms = computed(
+ () =>
+ getSupportedPlatforms(gameData.value?.os_type, {
+ platformType: gameData.value?.platform_type,
+ }) as PlatformTransformType[]
+)
const pathMatches = (base: string, current: string) => {
if (!base || base === '/') return current === '/'
@@ -151,6 +162,57 @@ const has2depthButton = (gnbItem: GameDataMenu) => {
return gnbItem.children && Object.keys(gnbItem.children).length > 0
}
+const highlight = (text: string) => `${text}`
+
+const PLATFORM_LABEL_KEY: Record = {
+ pc: 'PC',
+ google_play: 'Google Play',
+ app_store: 'App Store',
+}
+
+const tmWithGameName = (key: string): string => {
+ const raw = tm(key)
+ if (typeof raw !== 'string') return ''
+
+ const withName = raw.replace(
+ /%게임명%/g,
+ highlight(gameData.value?.game_name || '')
+ )
+
+ const platformLines = supportedPlatforms.value
+ .map(platform => highlight(PLATFORM_LABEL_KEY[platform] as string))
+ .filter(Boolean)
+
+ return platformLines.length
+ ? `${withName}
${platformLines.join('
')}`
+ : withName
+}
+
+const showNotSupportedOSAlert = () => {
+ return modalStore.handleOpenAlert({
+ contentText: tmWithGameName('Alert_Not_SupportedOS'),
+ })
+}
+
+const handleStartClick = () => {
+ if (breakpoints.value.isDesktop) return
+
+ const target = device.isAndroid
+ ? 'google_play'
+ : device.isApple
+ ? 'app_store'
+ : null
+
+ if (!target || !supportedPlatforms.value.includes(target)) {
+ return showNotSupportedOSAlert()
+ }
+
+ const url = gameData.value?.market_json?.[target]?.url || ''
+ if (!url) return showNotSupportedOSAlert()
+
+ window.open(url, '_blank')
+}
+
const stopClickOutside = onClickOutside(navAreaRef, () => handleMenuClose())
// 화면 크기 변경 시 오버플로우 재계산
@@ -319,33 +381,37 @@ onBeforeUnmount(() => {
-
- {{ gnb1depthButtonData?.btn_info?.txt_btn_name }}
-
-
-
- -
-
- {{ item.btn_info?.txt_btn_name }}
-
-
-
-
+
+
+ {{ gnb1depthButtonData?.btn_info?.txt_btn_name }}
+
+
+
+
+ -
+
+ {{ item.btn_info?.txt_btn_name }}
+
+
+
+
+