/* * [모바일] 관람객(B2C) 마이 — 내정보·내 티켓·내 주차권·언어·서버 상태·로그아웃. * 운영(B2B) 더보기(SCR-M3 more)의 관람객 경량판. 공통 키(more.*) 재사용 + vmy.* 최소 신설. */ import { Ionicons } from '@expo/vector-icons'; import { router } from 'expo-router'; import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { Avatar } from '../../components/Avatar'; import { Card } from '../../components/Card'; import { LanguageSelector } from '../../components/LanguageSelector'; import { useAuth } from '../../context/AuthContext'; import { api, isDegraded } from '../../lib/api'; import { API_BASE } from '../../lib/config'; import type { HealthDto } from '../../lib/types'; import { colors, radius, spacing, touch, type } from '../../theme'; export default function VisitorMyScreen() { const { t } = useTranslation(); const { user, signOut } = useAuth(); const [health, setHealth] = useState(t('common.loading')); const checkHealth = useCallback(async () => { try { const res = await api.get('/health', { anonymous: true }); setHealth(res?.status === 'UP' ? t('more.statusUp') : (res?.status ?? t('common.unknown'))); } catch (e) { setHealth(isDegraded(e) ? t('more.statusDown') : t('common.error')); } }, [t]); useEffect(() => { checkHealth(); }, [checkHealth]); async function onSignOut() { await signOut(); router.replace('/login'); } return ( {/* 내 정보 진입 */} router.push('/profile')} accessibilityRole="button" accessibilityLabel={t('more.openProfile')} > {user?.displayName ?? t('common.user')} {t('more.accountGeneral')} {/* 바로가기 — 내 티켓 · 내 주차권 */} router.push('/(visitor)/tickets')} /> router.push('/(visitor)/onsite')} /> {/* 언어 */} {/* 서버 상태 */} {t('more.signOut')} {t('more.version')} ); } function NavRow({ icon, title, desc, onPress, }: { icon: keyof typeof Ionicons.glyphMap; title: string; desc: string; onPress: () => void; }) { return ( {title} {desc} ); } function Row({ label, value }: { label: string; value: string }) { return ( {label} {value} ); } const styles = StyleSheet.create({ scroll: { padding: spacing.md, gap: spacing.md, paddingBottom: 40 }, meRow: { flexDirection: 'row', alignItems: 'center' }, name: { fontSize: type.h3.fontSize, fontWeight: '700', color: colors.neutral900 }, meta: { marginTop: 4, color: colors.neutral500, fontSize: type.caption.fontSize }, navRow: { flexDirection: 'row', alignItems: 'center', gap: 12, backgroundColor: colors.white, borderWidth: 1, borderColor: colors.neutral200, borderRadius: radius.md, padding: spacing.md, }, navIcon: { width: 40, height: 40, borderRadius: radius.sm, backgroundColor: colors.primary050, alignItems: 'center', justifyContent: 'center', }, navTitle: { fontSize: type.h3.fontSize, fontWeight: '600', color: colors.neutral900 }, navDesc: { fontSize: type.caption.fontSize, color: colors.neutral500 }, row: { flexDirection: 'row', justifyContent: 'space-between', gap: 12, paddingVertical: 4 }, rowLabel: { color: colors.neutral500, fontSize: type.body.fontSize }, rowValue: { flex: 1, textAlign: 'right', color: colors.neutral900, fontSize: type.body.fontSize }, divider: { height: 1, backgroundColor: colors.neutral200, marginVertical: 6 }, healthRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }, signOut: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, minHeight: touch.min, borderRadius: radius.sm, borderWidth: 1, borderColor: colors.error, }, signOutText: { color: colors.error, fontSize: type.h3.fontSize, fontWeight: '600' }, version: { textAlign: 'center', color: colors.neutral500, fontSize: type.caption.fontSize }, });